文件锁-fcntl flock lockf

这三个函数的作用都是给文件加锁,那它们有什么区别呢?

首先flock和fcntl是系统调用,而lockf是库函数。lockf实际上是fcntl的封装,所以lockf和fcntl的底层实现是一样的,对文件加锁的效果也是一样的。后面分析不同点时大多数情况是将fcntl和lockf放在一起的。

下面首先看每个函数的使用,从使用的方式和效果来看各个函数的区别。

1. flock

l 函数原型

#include<sys/file.h>

int flock(int fd, int operation);  // Apply or remove an advisory lock on the open file specified by fd,只是建议性锁

其中fd是系统调用open返回的文件描述符,operation的选项有:

LOCK_SH :共享锁

LOCK_EX :排他锁或者独占锁

LOCK_UN : 解锁。

LOCK_NB:非阻塞(与以上三种操作一起使用)

关于flock函数,首先要知道flock函数只能对整个文件上锁,而不能对文件的某一部分上锁,这是于fcntl/lockf的第一个重要区别,后者可以对文件的某个区域上锁。

其次,flock只能产生劝告性锁。我们知道,linux存在强制锁(mandatory lock)和劝告锁(advisory lock)。所谓强制锁,比较好理解,就是你家大门上的那把锁,最要命的是只有一把钥匙,只有一个进程可以操作。所谓劝告锁,本质是一种协议,你访问文件前,先检查锁,这时候锁才其作用,如果你不那么kind,不管三七二十一,就要读写,那么劝告锁没有任何的作用。而遵守协议,读写前先检查锁的那些进程,叫做合作进程。

再加上,flock可以有共享锁和排它锁,lockf只支持排它锁,但是fcntl里面参数flock可以有RDLCK读锁。

再次,flock和fcntl/lockf的区别主要在fork和dup时候的区别,后面有讲。

另外,flock不能再NFS文件系统上使用,如果要在NFS使用文件锁,请使用fcntl。

然后,后面讲了一堆fork和dup之后flock的表现。可以去看原文。

2. lockf与fcntl

l 函数原型

#include <unistd.h>

int lockf(int fd, int cmd, off_t len);

fd为通过open返回的打开文件描述符。

cmd的取值为:

F_LOCK:给文件互斥加锁,若文件以被加锁,则会一直阻塞到锁被释放。

F_TLOCK:同F_LOCK,但若文件已被加锁,不会阻塞,而回返回错误。

F_ULOCK:解锁。

F_TEST:测试文件是否被上锁,若文件没被上锁则返回0,否则返回-1。

len:为从文件当前位置的起始要锁住的长度。

通过函数参数的功能,可以看出lockf只支持排他锁,不支持共享锁。

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );(用法:int ret = fcntl(fd, F_SETLKW, &lock);)

其实的lock就是下面的数据结构:

struct flock {

...

short l_type;/* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */

short l_whence; /* How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END */

off_t l_start;   /* Starting offset for lock */

off_t l_len;     /* Number of bytes to lock */

pid_t l_pid; /* PID of process blocking our lock (F_GETLK only) */

...

};

文件记录加锁相关的cmd 分三种:

F_SETLK:申请锁(读锁F_RDLCK,写锁F_WRLCK)或者释放所(F_UNLCK),但是如果kernel无法将锁授予本进程(被其他进程抢了先,占了锁),不傻等,返回error。

F_SETLKW:和F_SETLK几乎一样,唯一的区别,这厮是个死心眼的主儿,申请不到,就傻等。

F_GETLK:这个接口是获取锁的相关信息: 这个接口会修改我们传入的struct flock。

通过函数参数功能可以看出fcntl是功能最强大的,它既支持共享锁又支持排他锁,即可以锁住整个文件,又能只锁文件的某一部分。

下面看fcntl/lockf的特性:

1. 可递归(同flock)

2. 加读锁(共享锁)文件必须是读打开的,加写锁(排他锁)文件必须是写打开。

3. 进程不能使用F_GETLK命令来测试它自己是否再文件的某一部分持有一把锁。

4. 进程终止时,他所建立的所有文件锁都会被释放(同flock)。

5. 任何时候关闭一个描述符时,则该进程通过这一描述符可以引用的文件上的任何一把锁都被释放(这些锁都是该进程设置的),这一点与flock不同。

fd1 = open(pathname, …);
lockf(fd1, F_LOCK, 0);
fd2 = dup(fd1);
close(fd2);
则在close(fd2)后,再fd1上设置的锁会被释放,如果将dup换为open,以打开另一描述符上的同一文件,则效果也一样。
fd1 = open(pathname, …);
lockf(fd1, F_LOCK, 0);
fd2 = open(pathname, …);
close(fd2);

6. 由fork产生的子进程不继承父进程所设置的锁,这点与flock也不同。(因为flock创建的锁是和文件打开表项(struct file)相关联的,而不是fd,所以复制出了fd都可以操作这把锁,所以子进程继承了父进程的锁。flock里面要关闭所有复制出的fd,锁才会释放)

7. 在执行exec后,新程序可以继承原程序的锁,这点和flock是相同的。(如果对fd设置了close-on-exec,则exec前会关闭fd,相应文件的锁也会被释放)。

8. 支持强制性锁(跟flock不同)。下面会讲。

3. 两种锁的关系

那么flock和lockf/fcntl所上的锁有什么关系呢?答案时互不影响。测试程序如下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
int main(int argc, char **argv)
{
    int fd, ret;
    int pid;
    fd = open("./tmp.txt", O_RDWR);
    ret = flock(fd, LOCK_EX);
    printf("flock return ret : %d\n", ret);
    ret = lockf(fd, F_LOCK, 0);
    printf("lockf return ret: %d\n", ret);
    sleep(100);
    return 0;
}

测试结果如下:

$./a.out

flock return ret : 0

lockf return ret: 0

可见flock的加锁,并不影响lockf的加锁。两外我们可以通过/proc/locks查看进程获取锁的状态。

$ps aux | grep a.out | grep -v grep

123751   18849  0.0  0.0  11904   440 pts/5    S+   01:09   0:00 ./a.out

$sudo cat /proc/locks | grep 18849

1: POSIX  ADVISORY  WRITE 18849 08:02:852674 0 EOF

2: FLOCK  ADVISORY  WRITE 18849 08:02:852674 0 EOF

我们可以看到/proc/locks下面有锁的信息:我现在分别叙述下含义:

1) POSIX FLOCK 这个比较明确,就是哪个类型的锁。flock系统调用产生的是FLOCK,fcntl调用F_SETLK,F_SETLKW或者lockf产生的是POSIX类型,可见两种调用产生的锁的类型是不同的;

2) ADVISORY表明是劝告锁;

3) WRITE顾名思义,是写锁,还有读锁;

4) 18849是持有锁的进程ID。当然对于flock这种类型的锁,会出现进程已经退出的状况。

5) 08:02:852674表示的对应磁盘文件的所在设备的主设备好,次设备号,还有文件对应的inode number。

6) 0表示的是所的其实位置

7) EOF表示的是结束位置。 这两个字段对fcntl类型比较有用,对flock来是总是0 和EOF。

fcntl支持强制性锁:对一个特定文件打开其设置组ID位(S_ISGID),并关闭其组执行位(S_IXGRP),则对该文件开启了强制性锁机制。再Linux中如果要使用强制性锁,则要在文件系统mount时,使用-omand打开该机制。

见这篇文章:

http://blog.jobbole.com/16882/

用fcntl加锁:

#include <stdio.h>
#include <fcntl.h>

int main(int argc, char **argv) {
  if (argc > 1) {
    int fd = open(argv[1], O_WRONLY);
    if(fd == -1) {
      printf("Unable to open the file\n");
      exit(1);
    }
    static struct flock lock;

    lock.l_type = F_WRLCK;
    lock.l_start = 0;
    lock.l_whence = SEEK_SET;
    lock.l_len = 0;
    lock.l_pid = getpid();

    int ret = fcntl(fd, F_SETLKW, &lock);
    printf("Return value of fcntl:%d\n",ret);
    if(ret==0) {
      while (1) {
        scanf("%c", NULL);
      }
    }
  }
}

使用mount命令带“mand”参数来重新挂载根文件系统,如下所示。这将在文件系统级别使能强制锁功能。注意:你必须切换到root用户才能执行下面的命令。

# mount -oremount,mand /

在可执行的(file_lock所在的)目录中创建两个名为“advisory.txt”和“mandatory.txt”的文件。对于“mandatory.txt”使能Set-Group-ID,同时不使能Group-Execute-Bit,如下所示:

# touch advisory.txt
# touch mandatory.txt
# chmod g+s,g-x mandatory.txt

测试协同锁:

执行示例程序,以“advisory.txt”作为参数。

# ./file_lock advisory.txt
此程序将等待用户的输入。

从另一个终端或控制台,尝试输入以下命令行(不检查锁,直接输入):

# ls >>advisory.txt
在上面的例子中,ls命令会将其输出写入到advisory.txt文件中。即使我们获得了一个写入锁,仍然会有一些进程(非合作)能够往文件里写入数据。这就是所谓的“协同”锁。

测试强制锁:

再次执行示例程序,以“mandatory.txt”作为参数。

# ./file_lock mandatory.txt

从另一个终端或控制台,尝试输入以下命令行:

# ls >>mandatory.txt
在上面的例子中,ls命令在将其输出写入到mandatory.txt文件之前,会等待文件锁被删除。虽然它仍然是一个非合作进程,但强制锁起了作用。

原文地址:https://www.cnblogs.com/fchy822/p/9064144.html

时间: 2024-10-19 20:27:36

文件锁-fcntl flock lockf的相关文章

Linux文件锁学习-flock, lockf, fcntl

参考  linux中fcntl().lockf.flock的区别 这三个函数的作用都是给文件加锁,那它们有什么区别呢? 首先flock和fcntl是系统调用,而lockf是库函数.lockf实际上是fcntl的封装,所以lockf和fcntl的底层实现是一样的,对文件加锁的效果也是一样的.后面分析不同点时大多数情况是将fcntl和lockf放在一起的. 下面首先看每个函数的使用,从使用的方式和效果来看各个函数的区别. 1. flock l 函数原型 #include<sys/file.h> i

每天进步一点点——Linux编程中的文件锁之flock

转载请说明出处:http://blog.csdn.net/cywosp/article/details/30083015 1. 场景概述 在多线程开发中,互斥锁可以用于对临界资源的保护,防止数据的不一致,这是最为普遍的使用方法.那在多进程中如何处理文件之间的同步呢?我们看看下面的图: 图中所示的是两个进程在无同步的情况下同时更新同一个文件的过程,其主要的操作是: 1. 从文件中读取序号. 2. 使用这个序号完成应用程序定义的任务. 3. 递增这个序号并将其写回文件中. 从图中可得知两个进程读取分

文件操作篇 close creat dup dup2 fcntl flock fsync lseek mkstemp open read sync write

文件操作篇 close creat dup dup2 fcntl flock fsync lseek mkstemp open read sync write close(关闭文件) 相关函数 open,fcntl,shutdown,unlink,fclose 表头文件 #include<unistd.h> 定义函数 int close(int fd); 函数说明 当使用完文件后若已不再需要则可使用close()关闭该文件,二close()会让数据写回磁盘,并释放该文件所占用的资源.参数fd为

JSON与CSV的转换

思路: 先把多个JSON元素转换成数组,然后使用python的csv和json模块来处理. 范例格式: [ {     "description": null,     "image": {       "image_size": 20,       "os_family": "centos",       "platform": "linux",       &qu

简单实现并发:python concurrent模块

可以使用python 3中的concurrent模块,如果python环境是2.7的话,需要下载https://pypi.python.org/packages/source/f/futures/futures-2.1.6.tar.gz#md5=cfab9ac3cd55d6c7ddd0546a9f22f453 此futures包即可食用concurrent模块. 官方文档:http://pythonhosted.org//futures/ 对于python来说,作为解释型语言,Python的解释

flock,lockf,flockfile,funlockfile

flock和lockf 从底层的实现来说,Linux的文件锁主要有两种:flock和lockf.需要额外对lockf说明的是,它只是fcntl系统调用的一个封装.从使用角度讲,lockf或fcntl实现了更细粒度文件锁,即:记录锁.我们可以使用lockf或fcntl对文件的部分字节上锁,而flock只能对整个文件加锁.这两种文件锁是从历史上不同的标准中起源的,flock来自BSD而lockf来自POSIX,所以lockf或fcntl实现的锁在类型上又叫做POSIX锁. 除了这个区别外,fcntl

python fcntl 文件锁

此模块只有在 unix 系统上才有,windows 没有. 文档地址: https://docs.python.org/3.7/library/fcntl.html https://www.docs4dev.com/docs/zh/python/3.7.2rc1/all/library-fcntl.html 多进程示例程序 import fcntl import os import time from multiprocessing import Pool def worker(): print

Linux文件锁flock

在多个进程同时操作同一份文件的过程中,很容易导致文件中的数据混乱,需要锁操作来保证数据的完整性,这里介绍的针对文件的锁,称之为“文件锁”-flock. flock,建议性锁,不具备强制性.一个进程使用flock将文件锁住,另一个进程可以直接操作正在被锁的文件,修改文件中的数据,原因在于flock只是用于检测文件是否被加锁,针对文件已经被加锁,另一个进程写入数据的情况,内核不会阻止这个进程的写入操作,也就是建议性锁的内核处理策略. flock主要三种操作类型: LOCK_SH,共享锁,多个进程可以

unix/linux 进程间文件锁

转自 http://www.cnblogs.com/hjslovewcl/archive/2011/03/14/2314333.html 有三种不同的文件锁,这三种都是“咨询性”的,也就是说它们依靠程序之间的合作,所以一个项目中的所有程序封锁政策的一致是非常重要的,当你的程序需要和第三方软件共享文件时应该格外地小心. 有 些程序利用诸如 FIlENAME.lock 的文件锁文件,然后简单地测试此类文件是否存在.这种方法显然不太好,因为当产生文件的进程被杀后,锁文件依然存在,这样文件也许会被永久锁