这三个函数的作用都是给文件加锁,那它们有什么差异呢?首要flock和fcntl是体系调用,而lockf是库函数。lockf实际上是fcntl的封装,所以lockf和fcntl的底层实现是相同的,对文件加锁的作用也是相同的。后边剖析不同点时大多数情况是将fcntl和lockf放在一同的。下面首要看每个函数的运用,从运用的方法和作用来看各个函数的差异
1.flock
函数原型
intflock(intfd,intoperation);//Applyorremoveanadvisorylockontheopenfilespecifiedbyfd,仅仅建议性锁
其间fd是体系调用open回来的文件描述符,operaTIon的选项有:
LOCK_SH:同享锁
LOCK_EX:排他锁或许独占锁
LOCK_UN:解锁。
LOCK_NB:非堵塞(与以上三种操作一同运用)
关于flock函数,首要要知道flock函数只能对整个文件上锁,而不能对文件的某一部分上锁,这是于fcntl/lockf的第一个重要差异,后者能够对文件的某个区域上锁。其次,flock只能发生劝说性锁。咱们知道,linux存在强制锁(mandatorylock)和劝说锁(advisorylock)。所谓强制锁,比较好了解,便是你家大门上的那把锁,最要命的是只要一把钥匙,只要一个进程能够操作。所谓劝说锁,实质是一种协议,你拜访文件前,先检查锁,这时候锁才其作用,假如你不那么kind,不管三七二十一,就要读写,那么劝说锁没有任何的作用。而恪守协议,读写前先检查锁的那些进程,叫做协作进程。再次,flock和fcntl/lockf的差异主要在fork和dup。
(1)flock创立的锁是和文件翻开表项(structfile)相关联的,而不是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
#include
#include
#include
#include
int main (int argc, char ** argv)
{
int ret;
int fd1 = open(“./tmp.txt”,O_RDWR);
int fd2 = dup(fd1);
printf(“fd1: %d, fd2: %d\n”, fd1, fd2);
ret = flock(fd1,LOCK_EX);
printf(“get lock1, ret: %d\n”, ret);
ret = flock(fd2,LOCK_EX);
printf(“get lock2, ret: %d\n”, ret);
return 0;
}
运转成果如图,对fd1上锁,并不影响程序经过fd2上锁。关于父子进程,参阅程序二。
程序二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include
#include
#include
#include
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: %d\n”,fd, ret);
sleep(10);
printf(“chile exit\n”);
exit(0);
}
ret = flock(fd,LOCK_EX);
printf(“parent get lock, fd: %d, ret: %d\n”, fd, ret);
printf(“parent exit\n”);
return 0;
}
运转成果如图,子进程持有锁,并不影响父进程经过相同的fd获取锁,反之亦然。
(2)运用open两次翻开同一个文件,得到的两个fd是独立的(由于底层对应两个file目标),经过其间一个加锁,经过另一个无法解锁,而且在前一个解锁前也无法上锁。测验程序如程序三:
程序三
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include
#include
#include
#include
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: %d\n”, fd1, fd2);
ret = flock(fd1,LOCK_EX);
printf(“get lock1, ret: %d\n”, ret);
ret = flock(fd2,LOCK_EX);
printf(“get lock2, ret: %d\n”, ret);
return 0;
}
成果如图,经过fd1获取锁后,无法再经过fd2获取锁。
(3)运用exec后,文件锁的情况不变。
(4)flock不能再NFS文件体系上运用,假如要在NFS运用文件锁,请运用fcntl。
(5)flock锁可递归,即经过dup或许或许fork发生的两个fd,都能够加锁而不会发生死锁。
2.lockf与fcntl
函数原型
#include
intlockf(intfd,intcmd,off_tlen);
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 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:这个接口是获取锁的相关信息:这个接口会修正咱们传入的structflock。
经过函数参数功用能够看出fcntl是功用最强壮的,它既支撑同享锁又支撑排他锁,即能够锁住整个文件,又能只锁文件的某一部分。
下面看fcntl/lockf的特性:
(1)上锁可递归,假如一个进程对一个文件区间现已有一把锁,后来进程又妄图在同一区间再加一把锁,则新锁将替换老锁。
(2)加读锁(同享锁)文件有必要是读翻开的,加写锁(排他锁)文件有必要是写翻开。
(3)进程不能运用F_GETLK指令来测验它自己是否再文件的某一部分持有一把锁。F_GETLK指令界说阐明,回来信息指示是否现存的锁阻挠调用进程设置它自己的锁。由于,F_SETLK和F_SETLKW指令总是替换进程的现有锁,所以调用进程绝不会堵塞再自己持有的锁上,所以F_GETLK指令绝不会陈述调用进程自己持有的锁。
(4)进程停止时,他所树立的一切文件锁都会被开释,队医flock也是相同的。
(5)任何时候封闭一个描述符时,则该进程经过这一描述符能够引证的文件上的任何一把锁都被开释(这些锁都是该进程设置的),这一点与flock不同。如:
1
2
3
4
fd1 = open(pathname, …);
lockf(fd1, F_LOCK, 0);
fd2 = dup(fd1);
close(fd2);
则在close(fd2)后,再fd1上设置的锁会被开释,假如将dup换为open,以翻开另一描述符上的同一文件,则作用也相同。
1
2
3
4
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
#include
#include
#include
#include
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
flockreturnret:0
lockfreturnret:0
可见flock的加锁,并不影响lockf的加锁。两外咱们能够经过/proc/locks检查进程获取锁的情况。
$psaux|grepa.out|grep-vgrep
123751188490.00.011904440pts/5S+01:090:00./a.out
$sudocat/proc/locks|grep18849
1:POSIXADVISORYWRITE1884908:02:8526740EOF
2:FLOCKADVISORYWRITE1884908:02:8526740EOF
咱们能够看到/proc/locks下面有锁的信息:我现在别离叙说下意义:
1)POSIXFLOCK这个比较清晰,便是哪个类型的锁。flock体系调用发生的是FLOCK,fcntl调用F_SETLK,F_SETLKW或许lockf发生的是POSIX类型,有次可见两种调用发生的锁的类型是不同的;
2)ADVISORY标明是劝说锁;
3)WRITE望文生义,是写锁,还有读锁;
4)18849是持有锁的进程ID。当然关于flock这种类型的锁,会呈现进程现已退出的情况。
5)08:02:852674表明的对应磁盘文件的地点设备的主设备好,次设备号,还有文件对应的inodenumber。
6)0表明的是所的其实方位
7)EOF表明的是完毕方位。这两个字段对fcntl类型比较有用,对flock来是总是0和EOF。