flock比fcntl更简单可靠,因其基于内核文件描述符锁、自动释放、无死锁残留,且避免竞态条件;而fcntl需手动管理、易出错、不保证kill-9后释放。

用 flock 比 fcntl 更简单可靠
Linux 下限制单实例配置文件访问,flock 是首选。它基于内核文件描述符级锁,自动随进程退出释放,不残留死锁;而 fcntl 的 F_SETLK 需手动管理 struct flock、容易因 fork 或多线程出错,且锁粒度更细、反而易误用。
-
flock锁住的是整个文件(不管 open 多少次),fcntl锁的是字节范围,一不留神就锁错位置 - 如果程序崩溃或被
kill -9,flock会自动释放,fcntl不保证(尤其在共享 fd 场景下) - 同一进程多次
flock(fd, LOCK_EX)不会阻塞,但重复fcntl加锁可能返回EAGAIN或覆盖前锁
示例:打开配置文件后立即加锁
int fd = open("/etc/myapp.conf", O_RDWR);
if (fd == -1) { /* handle error */ }
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
if (errno == EWOULDBLOCK) {
// 已被其他实例占用
exit(1);
}
}为什么不能只靠文件存在性或 PID 文件
检查 /var/run/myapp.pid 或写临时锁文件,本质是竞态条件(TOCTOU):判断文件不存在 → 创建 → 中间可能被另一个进程插入。真实部署中,这会导致两个实例同时读写同一配置文件,破坏一致性。
- PID 文件无法验证原进程是否存活(进程已死但 PID 文件未删)
- 没有原子性保障:
open(... O_CREAT | O_EXCL)只对创建有效,对已有文件无效 - 即使加了信号处理清理 PID,
kill -9仍会让清理失效
真正可靠的互斥必须依赖内核提供的原子原语,flock 正是为此设计。
立即学习“C++免费学习笔记(深入)”;
flock 在不同场景下的行为差异
flock 行为受文件系统和 open 方式影响,不是所有情况都等价。
- 仅对 regular file 有效;NFS 上部分版本不支持,需确认
mount选项含nolock(禁用) - 必须在同一个挂载点、同一文件 inode 上才生效;符号链接指向不同路径 ≠ 同一锁
- 子进程继承 fd 时也继承锁,但
fork后父子进程共持一把锁;exec后锁保留,close(fd)才释放 -
LOCK_SH和LOCK_EX互斥,但多个LOCK_SH可共存——读配置可用共享锁,写才用独占
实际使用时最容易漏掉的三件事
写完 flock 就以为万事大吉?生产环境里,下面这些点常被跳过,导致锁形同虚设。
- 没检查
open返回值——如果配置文件权限不足,open失败,后续flock对 -1 fd 调用会触发 undefined behavior - 忽略
LOCK_NB:默认flock(fd, LOCK_EX)会阻塞,启动脚本或 systemd 服务可能卡住,应始终加LOCK_NB并处理失败 - 锁文件和配置文件不是同一个 fd:比如先
open配置文件用于读取,再open另一个路径做锁文件——这根本锁不住配置操作本身
锁的有效性只绑定在你调用 flock 的那个 fd 上,且只对这个 fd 后续的 read/write 生效。想保护配置解析逻辑,就得确保所有读写都走这个 fd。



















