flock锁是按OFD还是按进程-文件维度生效?
Linux man 2 flock 相关说明
[L1]
flock() 创建的锁与打开文件描述符(OFD,见open(2))相关联。这意味着通过fork(2)或dup(2)等创建的重复文件描述符指向同一锁,可通过任意此类描述符修改或释放锁。此外,锁会在任意重复描述符执行显式LOCK_UN操作,或所有此类描述符关闭时释放。[L2]
若进程通过open(2)(或类似调用)获取同一文件的多个文件描述符,flock()会将这些描述符视为独立个体。通过其中一个描述符尝试锁定文件时,可能会被进程已通过另一描述符施加的锁阻止。[L3]
进程对同一文件只能持有一种类型的锁(共享或排他)。对已锁定文件的后续flock()调用会将现有锁转换为新的锁模式。
基础概念理解
打开文件描述符(OFD)是调用open文件时创建的实体,文件描述符是对OFD的引用,即传递给系统调用的整数,同一OFD可对应多个文件描述符。
推断与困惑点
从[L1]和[L2]可推断:flock锁是按OFD维度生效的。即无论是否在同一进程中,两次尝试对同一文件施加LOCK_EX锁时,其中一次会阻塞;若已通过某OFD持有LOCK_SH锁,再通过另一OFD尝试施加LOCK_EX锁,也会阻塞,且不受进程边界影响。
但[L3]的描述带来困惑:若通过一个OFD持有文件的LOCK_SH锁,同一进程中再次打开该文件得到第二个OFD,然后对第二个OFD施加LOCK_EX锁,难道第一个锁会被转换为排他锁?这显然不符合逻辑,因为同一文件会出现多个“排他”锁。
到底是文档措辞不够严谨,还是对该机制的理解存在偏差?
FreeBSD flock手册相关说明
[B1]
锁是针对文件而非文件描述符的。即通过dup(2)或fork(2)复制的文件描述符不会生成多个锁实例,而是指向同一锁的多个引用。若持有文件锁的进程fork后,子进程显式解锁文件,父进程也会失去锁。
若仅按此描述理解,之前对[L1]和[L2]的理解将完全被推翻,会认为同一进程持有LOCK_SH锁时,对该文件施加LOCK_EX锁仅会升级LOCK_SH锁——不存在多个排他锁的问题,因为它们只是同一锁的引用。
核心结论
Linux与FreeBSD的flock实现存在本质差异,且Linux手册的[L3]措辞确实存在模糊:
- Linux下的flock锁是OFD级别的:
- [L3]的真实适用场景是同一OFD的引用(如dup/fork生成的文件描述符):当通过这些引用调用flock时,会直接修改当前OFD关联的锁模式,不会产生互斥。
- 而[L2]明确的不同OFD场景下,同一进程内的多个OFD对同一文件的锁是完全独立的,互斥规则和跨进程一致。比如你用两个不同OFD打开同一文件,先给第一个加
LOCK_SH,再给第二个加LOCK_EX,操作会直接阻塞,不会触发[L3]描述的锁转换。
- FreeBSD下的flock锁是文件级别的:
同一进程对同一文件的所有文件描述符共享同一个锁实例,不存在不同OFD之间的互斥,锁模式的升级直接作用于这个全局锁,不会出现矛盾情况。
你的初始推断(Linux下flock锁按OFD维度生效)是正确的,[L3]的描述因未明确适用范围导致了歧义。
内容的提问来源于stack exchange,提问作者Brendan

