Linux文件锁和fcntl系统调用

1.Linux 中的文件锁

Linux 支持的文件锁技术主要包括劝告锁(advisory lock)和强制锁(mandatory lock)这两种。此外,Linux 中还引入了两种强制锁的变种形式:共享模式强制锁(share-mode mandatory lock)和租借锁(lease)。

在 Linux 中,不论进程是在使用劝告锁还是强制锁,它都可以同时使用共享锁和排他锁(又称为读锁和写锁)。多个共享锁之间不会相互干扰,多个进程在同一时刻可以对同一个文件加共享锁。但是,如果一个进程对该文件加了排他锁,那么其他进程则无权再对该文件加共享锁或者排他锁,直到该排他锁被释放。所以,对于同一个文件来说,它可以同时拥有很多读者,但是在某一特定时刻,它只能拥有一个写者,它们之间的兼容关系如表所示。

                锁间的兼容关系


是否满足请求
当前加上的锁
共享锁
排他锁
共享锁
排他锁

共享锁=读锁,排它锁=写锁

也就是说当前文件没有加锁时,此时共享锁和排他锁都能够加到此文件上

当前文件有共享锁时,此时只能在文件上加共享锁(同一个文件可以拥有很多读者),不能加排他锁

当前文件有排他锁时,不能在文件上继续加锁


进行对已加强制锁的文件进行操作时的行为

当前锁类型 阻塞读 阻塞写 非阻塞读 非阻塞写
读锁 正常读取数据 阻塞 正常读取数据 EAGAIN
写锁 阻塞 阻塞 EAGAIN EAGAIN

当前文件已经有一个读锁时,再加一个读锁(阻塞读,例如:fcntl(fd, F_SETLKW, &flock);非阻塞读:fcntl(fd, F_SETLK, &flock))是可以加上去的;如果再加一个写锁(阻塞写:导致阻塞;非阻塞写:返回errorno)

当文件有一个写锁时,同理。

文件中的每个字节在任一时刻只能拥有一种类型的锁:共享锁(读锁)、独占锁(写锁)或解锁。


2.实现文件锁

Linux可以通过这几种方式来实现文件锁功能:使用fcnt1系统调用和使用lockf调用以及flock。
我们将主要介绍fcnt1接口,因为它是最常使用的接口。lockf和fcnt1非常相似,在Linux中,它一般作为fcnt1的备选接口。但是,fcnt1和lockf的锁定机制不能同时工作:它们使用不同的底层实现,
因此决不要混合使用这两种类型的调用,而应坚持使用其中的一种。

fcntl不仅能锁整个文件,还能选择锁定这个文件的某一部分字节。
fcntl函数原型
int fcntl(int fildes, int command, ...);
fildes表示打开的文件描述符,command表示3个设置文件锁的选项
F_GETLK
F_SETLK
F_SETLKW
当使用这三个command的时候,第三个参数必须是一个指向flock结构的指针,所以实际的函数原型应为
int fcntl(int fildes, int command, struct flock *flock_structure);
struct flock(文件锁)结构依赖具体的实现,但它至少包含下述成员:
short l_type:取值定义在头文件fcnt1.h中:F_RDLCK(读锁)、F_UNLCK(解锁)、F_WRLCK(写锁)
short l_whence:取值必须是SEEK_SET、SEEK_CUR、SEEK_END(在头文件unistd.h中定义)中的一个。它们分别对应于文件头、当前位置和文件尾。
off_t l_start:相对于l_whence偏移的字节
off_t l_len:从l_start开始的字节数,为0表示文件末尾
pid_t l_pid:持有当前锁的进程pid

当把command设置为F_GETLK时,可以用flock_structure所描述的锁来测试fildes所代表的文件,看flock_structure锁描述的锁是否可以加到fildes所代表的文件上去。不可以加锁:把当前文件已经存在的锁写入flock_structure中,覆盖你开始设置的flock_structure;可以加锁:flock_structure的l_type变为F_UNLCK,并保持flock_structure中其他信息不变返回。(不会真正加锁,仅仅用来判断某个锁是否可以加上去,不能判断文件是否已经上锁)
例如:fcntl(fildes, F_GETLK, &flock_structure);
函数返回非-1表示调用成功(并不是说锁可以加上去),必须检查flock_structure结构的内容来判断其是否被修改过。因为l_pid的值被设置成持有锁的进程(如果有的话)的标识符,所以通过检查这个字段就可以很方便地判断出flock_structure结构是否被修改过(调用fcntl前,先把flock_structure的l_pid设置为-1(进程编号不会小于0),如果调用之后这个值改变了,说明是不能加锁的;也可以判断l_type,如果调用完后变成了F_UNLCK,那就说明可以加锁)。


command=F_SETLK时,对fildes指向的文件的某个区域加锁或解锁。加锁类型由l_type决定,区域由l_whence,l_start,l_len共同决定,这个函数立即返回成功(非-1值)或失败(-1)

command=F_SETLKW时,与上面的F_SETLK基本相同,只是在无法为文件添加锁时,函数将阻塞,直到可以添加锁或者获取到某个信号。


程序对某个文件拥有的所有锁都将在相应的文件描述符被关闭时自动清除。在程序结束时也会自动清除各种锁。


3.死锁

在讨论锁定时如果未提到死锁的危险,那么这个讨论就不能算是完整的。假设两个程序想要更新同一个文件。它们需要同时更新文件中的字节1和字节2。程序A选择首先更新字节2,然后再更新字节1。程序B则是先更新字节1,然后才是字节2。 两个程序同时启动。程序A锁定字节2,而程序B锁定字节1。然后程序A尝试锁定字节1,但因为这个字节已经被程序B锁定,所以程序A将在那里等待。接着程序B尝试锁定字节2,但因为这个字节已经被程序A锁定,所以程序B也将在那里等待。 这种两个程序都无法继续执行下去的情况,就被称为死锁(deadlock或deadly embrace)。这个问题在数据库应用程序中很常见,当许多用户频繁访问同一个数据时就很容易发生死锁。大多数的商业关系型数据库都能够检测到死锁并自动解开,但Linux内核不行。这时就需要采取一些外部干涉手段,例如强制终止其中一个程序来解决这个问题。 程序员必须对这种情况提高警惕。当有多个程序都在等待获得锁时,你就需要非常小心地考虑是否会发生死锁。在本例中,死锁是非常容易避免的:两个程序只需要使用相同的顺序来锁定它们需要的字节或锁定一个更大的区域即可。


Linux 2.6 中的文件锁:http://www.ibm.com/developerworks/cn/linux/l-cn-filelock/

fcntl函数:http://qcyhzwq001.lofter.com/tag/%E5%87%BD%E6%95%B0

获取锁定信息:http://docs.oracle.com/cd/E19253-01/819-7052/fileio-ex-1/index.html

记录上锁:http://www.cnblogs.com/siguoya/p/3512051.html


版权声明:本文为博主原创文章,未经博主允许不得转载。

你可能感兴趣的:(linux,读锁,文件锁,fcntl,写锁)