flock和fcntl的区别

来源:互联网 发布:知乎周刊pdf 编辑:程序博客网 时间:2024/06/05 22:38

原文出处:http://www.jb51.net/article/90255.htm

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

 1. flock

函数原型

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

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

     1.LOCK_SH :共享锁

     2.LOCK_EX :排他锁或者独占锁

     3.LOCK_UN : 解锁。

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

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

(1) flock创建的锁是和文件打开表项(struct file)相关联的,而不是fd。这就意味着复制文件fd(通过fork或者dup)后,那么通过这两个fd都可以操作这把锁(例如通过一个fd加锁,通过另一个fd可以释放锁),也就是说子进程继承父进程的锁。但是上锁过程中关闭其中一个fd,锁并不会释放(因为file结构并没有释放),只有关闭所有复制出的fd,锁才会释放。测试程序入程序一。

程序一

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/file.h>
  
int main (int argc, char ** argv)
  
{
  
  int ret;
  
  int fd1 =open("./tmp.txt",O_RDWR);
  
  int fd2 = dup(fd1);
  
  printf("fd1: %d, fd2: %dn", fd1, fd2);
  
  ret = flock(fd1,LOCK_EX);
  
  printf("get lock1, ret: %dn", ret);
  
  ret = flock(fd2,LOCK_EX);
  
  printf("get lock2, ret: %dn", ret);
  
  return0;

 

运行结果如图,对fd1上锁,并不影响程序通过fd2上锁。对于父子进程,参考程序二。

程序二

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
  
#include <unistd.h>
  
#include <stdlib.h>
  
#include <sys/file.h>
  
int main (int argc, char ** argv)
  
{
  
  int ret;
  
  int pid;
  
  int fd =open("./tmp.txt",O_RDWR);
  
  if((pid = fork()) == 0){
  
    ret = flock(fd,LOCK_EX);
  
    printf("chile get lock, fd: %d, ret: %dn",fd, ret);
  
    sleep(10);
  
    printf("chile exitn");
  
    exit(0);
  
  }
  
  ret = flock(fd,LOCK_EX);
  
  printf("parent get lock, fd: %d, ret: %dn", fd, ret);
  
  printf("parent exitn");
  
  return0;
  
}

运行结果如图,子进程持有锁,并不影响父进程通过相同的fd获取锁,反之亦然。

(2)使用open两次打开同一个文件,得到的两个fd是独立的(因为底层对应两个file对象),通过其中一个加锁,通过另一个无法解锁,并且在前一个解锁前也无法上锁。测试程序如程序三:

程序三 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
  
#include <unistd.h>
  
#include <stdlib.h>
  
#include <sys/file.h>
  
int main (int argc, char ** argv)
  
{
  
  int ret;
  
  int fd1 =open("./tmp.txt",O_RDWR);
  
  int fd2 =open("./tmp.txt",O_RDWR);
  
  printf("fd1: %d, fd2: %dn", fd1, fd2);
  
  ret = flock(fd1,LOCK_EX);
  
  printf("get lock1, ret: %dn", ret);
  
  ret = flock(fd2,LOCK_EX);
  
  printf("get lock2, ret: %dn", ret);
  
  return0;
  
}

结果如图,通过fd1获取锁后,无法再通过fd2获取锁。

(3) 使用exec后,文件锁的状态不变。

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

(5) flock锁可递归,即通过dup或者或者fork产生的两个fd,都可以加锁而不会产生死锁。

2. lockf与fcntl

函数原型
#include

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只支持排他锁,不支持共享锁。

?
1
2
3
4
5
6
7
8
9
10
11
12
#include 
#include 
int fcntl(int fd, int cmd, ... /* arg */ );
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 offsetforlock */ 
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) 上锁可递归,如果一个进程对一个文件区间已经有一把锁,后来进程又企图在同一区间再加一把锁,则新锁将替换老锁。

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

       (3) 进程不能使用F_GETLK命令来测试它自己是否再文件的某一部分持有一把锁。F_GETLK命令定义说明,返回信息指示是否现存的锁阻止调用进程设置它自己的锁。因为,F_SETLK和F_SETLKW命令总是替换进程的现有锁,所以调用进程绝不会阻塞再自己持有的锁上,于是F_GETLK命令绝不会报告调用进程自己持有的锁。

       (4) 进程终止时,他所建立的所有文件锁都会被释放,队医flock也是一样的。

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

?
1
2
3
4
5
6
7
fd1 =open(pathname, …);
  
lockf(fd1, F_LOCK, 0);
  
fd2 = dup(fd1);
  
close(fd2);

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

?
1
2
3
4
5
6
7
fd1 = open(pathname, …);
  
lockf(fd1, F_LOCK, 0);
  
fd2 = open(pathname, …);
  
close(fd2);

        (6) 由fork产生的子进程不继承父进程所设置的锁,这点与flock也不同。

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

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

3. 两种锁的关系

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#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 : %dn", ret);
  
  ret = lockf(fd, F_LOCK, 0);
  
  printf("lockf return ret: %dn", ret);
  
  sleep(100);
  
  return 0;
  
}

测试结果如下:

?
1
2
3
4
5
$./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。

以上就是linux中fcntl()、lockf和flock的区别的全部内容,希望本文对大家有所帮助。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 华为荣耀7x密码忘了怎么办 华为荣耀7x应用锁密码忘了怎么办 同学群里有人发低俗信息怎么办 小孩读英语绘本大人不会翻译怎么办 东西掉到手刹缝里面了怎么办 泰拉瑞亚猩红之地蔓延到家里怎么办 三星r.467玩dnf闪退怎么办 美团外卖不小心撞到汽车怎么办 如果你在战场上遇到华裔美军怎么办 衣服洗完了干了后发黄怎么办 毛衣起球怎么办学会这几个小妙招 小车没电了打不着火怎么办 老婆花钱大手大脚又要我给钱怎么办 住酒店手机id被劫持了怎么办 孩子老是送玩具给别的小朋友怎么办 老板总想和我谈人生怎么办gl 导师让用师姐的数据写论文怎么办 签了平面模特协议想违约怎么办 福州96年以前社保手册丢了怎么办 被老师缴的手机弄没了怎么办 户口在成都医保在德阳生孩子怎么办 微课掌上通看不到孩子班级圈怎么办 微课掌上通的录音错误是怎么办 微课视频录制ppt里面音乐怎么办 老师在街上和别人吵起来了怎么办 手机录屏传到爱剪辑变成竖屏怎么办 尔雅通识课程考试忘记做了怎么办 尔雅通识课过了课程完成时间怎么办 微信账号没冻结登录不了怎么办 微信解封电话号码没有电话号怎么办 ps中智能对象不能直接编辑怎么办 工资低奖金高银行流水不够怎么办 我水费交了自来水公司不给开怎么办 艺考文化分数差4分二本怎么办 学信网手机号换了密码忘记了怎么办 自考毕业证学信网上查不到怎么办 苹果手机自带浏览器证书过期怎么办 手机连接工行证书介质失贩怎么办 老公要带与小三生的孩子回家怎么办 小三怀孕打胎后还是很爱老公怎么办 小三怀孕开不到引产证明怎么办