flock申请的锁对象是处于 内核空间层的全局级别的open file table中的。
回忆一下OS是如何打开一个文件的?
首先一个文件对应一个fd,fd是维护在用户空间,通过索引对应到内核空间的fd表。程序是无法直接打开文件的,需要请内核帮忙。用户空间使用fd在内核中调用open函数,当然可以调用多次,调用多次在open file中就会记录多条记录。然后在内核空间的文件级别层的inode表找到对应的inode(这个inode表是从硬盘上copy过来的,并附加上一些额外的信息), 将此文件加载到虚拟内存中此时这个文件就被打开了。
注意:
内核层面的inode 表和open file表是一对多的关系。
fd表 中存在复制的fd,这两个fd可能指向同一个lock
如何理解man flock中的这段文字呢?
Locks created by flock() are associated with an open file table entry. This means that duplicate file descriptors (created by, for example, fork(2) or dup(2)) refer to the same lock, and this lock may be modified or released using any of these descriptors. Furthermore, the lock is released either by an explicit LOCK_UN operation on any of these duplicate descriptors, or when all such descriptors have been closed.
这段话主要说明了文件描述符上的锁和文件上的锁的区别 , 因为存在fork的子进程使用同一把锁。当手动释放锁文件时,open file table entry中的fd关联的锁消失。如果释放的是某个fd的锁,则锁还是存在并生效的。
open file table上维护的其他特殊信息
对于多个进程读同一文件都能正确工作。每个进程都有它自己的文件表项,其中也有它自己的当前文件位移量。但是,当多个进程写同一文件时,则可能产生预期不到的结果。
假定有两个独立的进程 A和B,都对同一文件进行添加操作。每个进程都已打开了该文件,但未使用 O _ A P P E N D标志。此时各数据结构之间的关系如图中所示一样。每个进程都有它自己的文件表项,但是共享一个 v节点表项。假定进程A调用了l s e e k,它将对于进程A的该文件的当前位移量设置为1 5 0 0字节(当前文件尾端处)。然后内核切换进程使进程 B运行。进程B执行l s e e k,也将其对该文件的当前位移量设置为 1 5 0 0字节(当前文件尾端处)。然后B调用w r i te,它将B的该文件的当前文件位移量增至 1 6 0 0。因为该文件的长度已经增加了,所以内核对 v节点中的当前文件长度更新为 1 6 0 0。然后,内核又进行进程切换使进程 A恢复运行。当A调用w r i t e时,就从其当前文件位移量 ( 1 5 0 0 )处将数据写到文件中去。这样也就代换了进程 B刚写到该文件中的数据。
--update 2022年3月24日14:04:00
劝告锁
flock和fcntl都属于劝告式锁(Advisory Lock),如果同步的进程遵循游戏规则,操作之前先申请锁,就能起到同步的作用;但是如果进程无视劝告式锁的存在,不遵循游戏规则,不申请锁直接操作文件或文件的某个区域,内核也不会阻止这种操作。