在 Linux 系统中,**文件锁(File Lock)**是多进程并发访问文件时的重要同步机制。无论是数据库、日志系统还是配置文件更新,都需要某种方式来避免多个进程同时修改同一文件造成数据破坏。
Linux 提供了两类主要的文件锁机制:
flock 锁
fcntl 锁(POSIX 锁)
除此之外,Linux 内核还支持:
文件描述符锁(open file description lock)
强制锁(Mandatory Lock)
本文将从 用户态 API → 内核数据结构 → 锁管理算法 → 内核源码流程 进行完整解析。
阅读完本文,你将理解:
flock 与 fcntl 的核心区别
Linux 文件锁的数据结构
内核如何管理锁冲突
锁的生命周期
内核源码执行路径
一、为什么需要文件锁
在 Linux 中,一个文件可能被多个进程同时访问。
例如:
Process A -------> file.txtProcess B -------> file.txt
如果两个进程同时写:
A: write("hello")B: write("world")
最终文件可能变成:heworld 或者:hellorld
这种 竞态条件(Race Condition) 会破坏数据一致性。
因此需要一种机制:
在某个进程访问文件时,阻止其他进程修改。
这就是 文件锁的作用。
二、Linux 文件锁分类
Linux 文件锁可以分为:
最常用的是:
三、flock 锁机制
1 flock API
#include <sys/file.h>intflock(int fd, int operation);
operation:
LOCK_SH 共享锁LOCK_EX 排他锁LOCK_UN 解锁LOCK_NB 非阻塞
主要接口:
int fd = open("test.txt", O_RDWR);flock(fd, LOCK_EX);write(fd, "hello", 5);flock(fd, LOCK_UN);
2 flock 锁特点
3 flock 锁
四、fcntl 锁机制
POSIX 推荐使用 fcntl 锁。
它比 flock 更灵活。
1 fcntl API
intfcntl(int fd, int cmd, struct flock *lock);
结构体:
struct flock { short l_type; short l_whence; off_t l_start; off_t l_len; pid_t l_pid;};
字段说明:
2 锁类型
F_RDLCK 读锁F_WRLCK 写锁F_UNLCK 解锁
3 区间锁
fcntl 最大特点:
可以锁定文件的某个区间
例如:
表示:锁住 0~100 字节
类似下图:
file.txt+------------------------+| 0 100 200 |+------------------------+| LOCKED | FREE |+------------------------+
五、flock vs fcntl
数据库通常使用:fcntl
六、Linux 内核中的文件锁架构
Linux 内核使用一个统一的锁管理系统。
核心结构:
定义在这两个文件中:
fs/locks.cinclude/linux/filelock.h
file_lock 结构
简化版:
struct file_lock { struct file *fl_file; loff_t fl_start; loff_t fl_end; unsigned char fl_type; struct list_head fl_list;};
关键字段解释下:
七、内核锁管理结构
Linux 使用 链表管理文件锁。
结构:
inode │ │ ▼i_flctx (lock context) │ ├── granted locks │ └── waiting locks
inode │ ▼+--------------------+| lock_context |+--------------------+| granted list || waiting list |+--------------------+
八、fcntl 加锁内核流程
用户调用:fcntl(fd, F_SETLK, &lock)
系统调用:sys_fcntl 调用流程如下:
sys_fcntl │ ▼do_fcntl │ ▼fcntl_setlk │ ▼posix_lock_file │ ▼locks_insert_lock
九、锁冲突检测
核心函数:locks_conflict()
逻辑是检查:1 锁区间是否重叠2 锁类型是否冲突
伪代码大概就是这样:
if overlap(start1,end1,start2,end2) if type conflict return conflict
十、锁区间重叠检测
示例:
Lock A0 ---- 100Lock B50 ---- 150
重叠:50 - 100 就会发生冲突。
十一、锁拆分(Lock Splitting)
如果锁部分重叠:
已有锁0 ---- 200,新锁: 50 ---- 100
内核会拆分:
0 --- 5050 ---100100--200
例如:
原始锁+----------------------+| LOCKED |+----------------------+拆分后+----+------+----+| L | L | L |+----+------+----+
十二、阻塞锁机制
如果使用:F_SETLKW 进程会阻塞等待。
等待队列:wait_queue
结构:waiting locks list
例如:
Process ALOCKEDProcess BWAIT
十三、锁释放流程
锁释放有两种方式:
1 主动解锁2 进程退出
主动解锁:fcntl(fd, F_UNLCK)
内核流程:
posix_lock_file │ ▼locks_delete_lock
进程退出
当进程退出:exit_files() 会调用:locks_remove_posix 释放锁。
十四、Open File Description Locks
很早的内核源码Linux 3.15 就引入了 OFD locks。
API:F_OFD_SETLK
这个API的特点:
主要解决:线程共享 fd 锁冲突的问题
十五、强制锁(Mandatory Lock)
Linux 默认锁是 建议锁(Advisory Lock)。
意思:程序必须主动检查锁
如果程序不检查:锁无效,强制锁:Mandatory Lock 需要:mount -o mand,文件权限:
setgid bitgroup execute off
但现在已经淘汰。
十六、NFS 文件锁
在 NFS 文件系统中:
锁通过:lockd 网络锁管理器实现。
fcntl │ ▼lockd │ ▼RPC │ ▼远程服务器
十七、死锁检测
POSIX 锁支持 死锁检测。
函数:posix_locks_deadlock()
检测流程:
A 等 BB 等 CC 等 A
发现循环:返回 EDEADLK
十八、锁性能问题
文件锁开销来源:
1 锁链表遍历2 区间冲突检测3 锁拆分
复杂度:O(n) 大量锁会影响性能。
十九、调试文件锁
查看锁:
1: POSIX ADVISORY WRITE 1234 fd:00:12345 0 EOF
字段:
二十、实际应用案例
数据库:
日志系统:syslog
邮件服务器:sendmail
都依赖文件锁。
写在最后
Linux 文件锁机制是一个 复杂但优雅的内核同步系统。
核心特点:
flock:简单文件锁
fcntl:POSIX 区间锁
OFD lock:现代锁机制
内核实现基于: struct file_lock
通过:
链表管理区间冲突检测锁拆分等待队列
实现多进程安全访问文件。
理解文件锁不仅有助于编写可靠程序,也能深入理解 Linux 内核 VFS 子系统设计思想。