关于LINUX的文件锁的一些心得

来源:互联网 发布:软件开发费用组成 编辑:程序博客网 时间:2024/05/23 14:42
在之前的一个项目中用到了文件锁,从网上粗略的查了下资料就匆匆忙忙的用上了,虽然当时也有一些疑惑,但是由于项目进度比较紧,也没有多想。现在,终于有点时间可以稍微静下心来看看这个文件锁到底是怎么一回事了。

从内核实现的角度来看,每当创建一把文件锁的时候,系统就会实例化一个struct file_lock对象,这个file_lock对象会记录锁的相关信息:如锁的类型(共享锁,独占锁)、拥有这把锁的进程号、锁的标识(租赁锁,阻塞锁,POSIX锁,FLOCK锁),等等。最后把这个file_lock对象插入到被锁文件的inode.i_flock链表中,就完成了对该文件的加锁功能。要是其它进程想要对同一个文件加锁,那么它在将file_lock对象插入到inode.i_flock之前,会遍历该链表,如果没有发现冲突的锁,就将其插入到链表尾,表示加锁成功,否则失败。
至于为什么要将inode与file_lock以链表的形式关联起来,主要是考虑到用户有时可以对同一个文件加多个文件锁。例如:我们可以对同一个文件加多个共享锁;或者我们可以同时对文件加POSIX锁和FLOCK锁,这两种锁分别对应flock()和fcntl()两种系统调用函数;再或者可以通过多次调用fcntl()对同一个文件中的多个内容块加上POSIX记录锁。

下面讲下POSIX锁和FLOCK锁的一些区别:


1. POSIX锁和FLOCK锁分别是通过fcntl()和flock()系统调用完成的。虽然实现的原理上都差不多,都是生成一个file_lock对象并插入inode文件锁链表,但是POSIX锁是支持针对某一段文件内容进行加锁的,而FLOCK锁不支持。

2. POSIX锁可以重复加锁,即同一个进程,可以对同一个文件多次加同样一把锁。例如:第一次我对A文件的一个0~10的内容块加了一把独占锁,那么第二次同一个进程中我一样可以对这个A文件的0~10的内容块再加一把独占锁,这个有点像是递归加锁,但是我解锁时只需要解一次。FLOCK锁则不同,如果你第一次对A文件加了一把独占锁,那么在同一个进程中你就不能对A文件再加一把锁了。这个区别其实只不过是在加锁的时候,遍历inode.i_flock链表时,发现存在PID相同的锁时,系统对于POSIX锁和FLOCK锁的具体处理手段不一样罢了。

3. 通过第2点,我们可以想象一下,POSIX锁和FLOCK锁在多线程环境下的不同。我们知道从Linux内核的视角来看,它是不区分所谓的进程和线程的,都不过是CPU调度队列中的一个个task_struct实例而已,所以不会对线程的场景进行专门的处理,也正以为如此,平时我们用的NPTL线程库也都是在用户态环境中模拟出来的,Linux内核并不直接支持。回到刚刚的话题,因为内核它在加锁的时候是看PID的,所以在内核看来多线程的加锁只不过是同一个进程(因为每个线程的PID都是一样的)在对同一个文件加多把锁。这样,多线程环境下的加锁行为就表现为:同一个进程中的多个线程可以对同一个文件加多次POSIX独占或共享锁,但是不可以对同一个文件加多次FLOCK独占锁(不过共享锁是可以加多次的)。

4. 在一个项目中使用了GPFS共享文件系统,我们在开发过程中发现,对于FLOCK锁只支持本地,而POSIX锁则可以支持跨主机加锁。例如:我们有两台独立的机器A和B,在A机器上有某个进程对文件f加POSIX独占锁,然后在B机器上当有某个进程想对f加POSIX独占锁时,就会失败。可是当我们使用FLOCK锁时,就发现两台机器对同一个文件加FLOCK锁是互不影响的,即A和B机器都可以对f加独占锁。针对这种情况,IBM工程师在邮件中给出的解释如下:



关于文件锁的其他一些细节性的东西,可以参考这篇文章:http://www.ibm.com/developerworks/cn/linux/l-cn-filelock/
对于一些更加底层的细节问题,如:加锁过程是只在VFS层操作还是涉及到具体的物理文件系统、GPFS上的POSIX文件锁如何做到跨机器有效,等等问题,可能就要参考源代码了。
原创粉丝点击