能,但仅适用于有亲缘关系的进程或共享同一文件描述符的场景;flock是文件描述符级锁,fork后子进程继承fd可同步,独立open则互不感知,跨进程应选fcntl或posix信号量。

flock 能不能用于多进程同步?
能,但只适用于有亲缘关系的进程或共享同一打开文件描述符的场景。它本质是“文件描述符级锁”,不是“文件名级锁”——flock 锁绑定在 fd 上,fork 后子进程继承该 fd,所以父子进程能靠同一个 flock 互斥;但两个独立 open("/path/to/file", ...) 得到的 fd,哪怕路径相同,锁也互不感知。
常见错误现象:flock(fd1, LOCK_EX) 在进程 A 成功,进程 B 却也能成功加锁,毫无阻塞——大概率是因为 B 是重新 open 的文件,不是复用 A 的 fd。
- 使用场景:父进程
open+flock后fork,子进程直接操作同一fd;或通过 Unix 域套接字传递fd给其他进程(较复杂,少用) - 跨进程(无亲缘)同步,请改用
fcntl文件锁或POSIX信号量(如sem_open) -
flock不保证 NFS 安全,某些 NFS 实现会静默忽略锁请求
怎么正确调用 flock?
核心是:先 open,再 flock,最后操作文件内容;释放锁靠 close(fd)(自动解锁),不是显式调用 flock(fd, LOCK_UN) ——虽然可以,但容易漏调或重复调。
示例(C++ 封装片段):
int fd = open("/tmp/mylock", O_CREAT | O_RDWR, 0644);
if (fd == -1) { /* handle error */ }
// 阻塞等待独占锁
if (flock(fd, LOCK_EX) == -1) { /* handle error */ }
// 此时可安全读写 /tmp/mylock 或其他资源(需自行约定)
write(fd, "data", 4);
// 关闭 fd 自动释放锁
close(fd); // ← 关键:别忘了 close,否则锁一直挂着
-
LOCK_SH/LOCK_EX/LOCK_UN是标准宏,头文件为<sys></sys> - 加锁失败时 errno 可能是
EWOULDBLOCK(非阻塞模式下)或EINTR(被信号中断),需检查并重试 - 不要对标准流(如
STDIN_FILENO)或管道调用flock,行为未定义
为什么 fork 后子进程还能看到父进程的 flock?
因为 flock 锁与内核中的“打开文件表项”(open file table entry)关联,而 fork 后父子进程共享该表项(即指向同一个 struct file)。只要任意一个进程还持有该 fd,锁就持续有效;只有所有持有该 fd 的进程都 close 了,锁才真正释放。
容易踩的坑:
- 子进程
exec前没close(fd)→ 锁被新程序意外持有着,父进程等不到释放 - 多线程中一个线程
close(fd),其他线程还在用该fd→use-after-close,可能崩溃或锁提前丢失 - 用
dup2替换fd后原fd未关 → 锁仍由 dup 出的fd持有,难追踪
C++ 封装 flock 时要注意什么?
C++ 没有内置 flock RAII 类,自己封装时必须严格匹配生命周期:构造时加锁(或尝试加锁),析构时仅当是最后一个拥有该 fd 的对象时才 close。但判断“是否最后一个”需要引用计数,且要处理 fork 后父子双方各自析构的问题——这几乎不可靠。
更稳妥的做法:
- 避免封装成通用类,只在明确单进程/父子进程模型下,用局部
fd+ 手动close控制 - 若必须 RAII,用
std::shared_ptr管理fd和引用计数,但需确保fork后子进程不共享该shared_ptr(fork会复制指针值,但底层引用计数不跨进程) - 生产环境建议直接用
boost::interprocess::file_lock(基于fcntl),它支持无亲缘进程、可命名、不依赖fd共享
实际用 flock 做多进程同步,前提是你们真共享了同一个 fd。多数人以为“同路径就同锁”,结果锁根本没生效——这点最常被忽略。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











