土法炼钢兴趣小组的算法知识备份

【操作系统百科】文件锁

文章导航

分类入口
os
标签入口
#flock#fcntl-lock#ofd-lock#lease#file-locking

目录

多进程共享文件时需要互斥。Linux 提供三代文件锁:flock(BSD)、fcntl POSIX 锁、OFD 锁。前两代各有语义地雷。

一、先看图

flowchart TD
    FLOCK[flock 锁<br/>per-file 粒度] --> BSD[BSD 语义<br/>fork 后子进程<br/>不继承]
    POSIX[POSIX fcntl 锁<br/>byte-range] --> POSIXSEM[per-process 语义<br/>close 任一 fd<br/>释放所有锁!]
    OFD[OFD 锁<br/>F_OFD_SETLK<br/>byte-range] --> OFDSEM[per-open-file 语义<br/>安全]
    classDef safe fill:#3fb95022,stroke:#3fb950,color:#adbac7;
    classDef danger fill:#f8514922,stroke:#f85149,color:#adbac7;
    classDef ok fill:#388bfd22,stroke:#388bfd,color:#adbac7;
    class FLOCK ok
    class POSIX,POSIXSEM danger
    class OFD,OFDSEM safe

二、flock(BSD 锁)

int fd = open("/tmp/data", O_RDWR);
flock(fd, LOCK_EX);      // 独占锁
// ... 操作文件
flock(fd, LOCK_UN);      // 解锁

特点:

缺点:无 byte-range → 只能锁整个文件。

NFS:早期 flock 在 NFS 上是 noop!现代 NFS v4 已修复。

三、POSIX fcntl 锁

struct flock fl = {
    .l_type = F_WRLCK,
    .l_whence = SEEK_SET,
    .l_start = 0,
    .l_len = 1024,       // 锁前 1024 字节
};
fcntl(fd, F_SETLK, &fl);

特点:

致命陷阱

close 任何一个指向同一文件的 fd 都会释放该进程持有的所有锁。

int fd1 = open("/tmp/data", O_RDWR);
fcntl(fd1, F_SETLK, &lock);  // 拿锁

int fd2 = open("/tmp/data", O_RDONLY);
close(fd2);                    // 锁被释放了!

这违反直觉,但是 POSIX 标准规定。

fork 后子进程不继承锁(因为是 per-process)。

四、OFD 锁(Open File Description Lock)

3.15+ 引入,修复 POSIX 锁的问题:

struct flock fl = {
    .l_type = F_WRLCK,
    .l_whence = SEEK_SET,
    .l_start = 0,
    .l_len = 1024,
    .l_pid = 0,           // OFD 锁忽略 pid
};
fcntl(fd, F_OFD_SETLK, &fl);

特点:

推荐:新代码应该用 OFD 锁,不用 POSIX fcntl 锁。

五、lease(租约)

fcntl(fd, F_SETLEASE, F_WRLCK);

含义:当其他进程打开这个文件时,内核给 lease 持有者发 SIGIO → 有时间窗做清理(如 Samba 的 oplock)。

用途:

六、mandatory lock(已退出)

mount -o mand /dev/sda1 /mnt
chmod g+s,g-x /mnt/file   // 设置 mandatory lock 标志

强制锁——没拿锁的进程 read/write 会被阻塞。

Linux 5.15 已删除 mandatory lock 支持

七、NFS 上的文件锁

NFS v3:lockd + rpc.statd 管理锁状态。客户端重启后需要 reclaim。

NFS v4:锁集成到协议里。lease-based。

NFSv4 的 delegation 类似 lease——server 委托客户端独占 cache。

八、死锁检测

POSIX 锁支持死锁检测:

fcntl(fd, F_SETLKW, &fl);  // 等待模式 + 死锁检测
// 如果检测到死锁返回 EDEADLK

flock 和 OFD 锁不做死锁检测。

九、选择指南

需求 推荐
简单互斥 flock
byte-range、多线程 OFD 锁
NFS 兼容 fcntl POSIX(NFS 原生支持)
遗留代码 fcntl POSIX(注意陷阱)
新代码 OFD 锁

十、观察

# 查看进程持有的锁
cat /proc/locks
# 1: POSIX  ADVISORY  WRITE 1234 08:01:12345 0 1023
# 2: FLOCK  ADVISORY  WRITE 5678 08:01:12345 0 EOF

# lslocks 友好版
lslocks

十一、小结


参考文献

工具


上一篇fd 表与 struct file 下一篇xattr/ACL/capabilities

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-27 · os

【操作系统百科】内存回收

Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。

2026-04-28 · os

【操作系统百科】交换

swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。

2026-05-03 · os

【操作系统百科】Slab/SLUB 分配器

buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。

2026-05-07 · os

【操作系统百科】用户态分配器

glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。


By .