flock 是系统调用而非 c++ 标准函数,需 #include 且仅 linux/macos 支持;它作用于文件描述符(fd),非文件路径或流对象,close(fd) 自动释放锁,windows 完全不支持。

为什么 flock 在 C++ 里不能直接用
flock 是系统调用,不是 C++ 标准库函数,头文件里没有声明。直接写 flock(fd, LOCK_EX) 会报 undefined reference to 'flock' —— 链接阶段找不到符号。
必须显式包含系统头文件并确保链接时有对应实现(Linux/macOS 基本都支持,Windows 完全不支持):
- 加
#include <sys></sys>(Linux/macOS) - 文件描述符
fd必须来自open(),不能是std::fstream的fileno()后随意传——std::ofstream构造的流可能带缓冲,fileno()返回的 fd 状态不可控 - 不要对
stdin/stdout/stderr的 fd 调用flock,行为未定义
flock 锁的是文件描述符,不是文件路径
同一个文件用两次 open() 得到两个不同 fd,flock 在这两个 fd 上加锁互不干扰——锁粒度是 fd,不是 inode。这点和 fcntl(F_SETLK) 不同,也常被误以为“没生效”。
典型误用场景:父进程 open → fork → 子进程继承 fd → 两边都 flock → 实际只有一把锁(因为 fd 相同)。但若子进程自己再 open() 一次,就绕过了父进程的锁。
立即学习“C++免费学习笔记(深入)”;
- 多进程安全的前提:所有进程操作同一文件时,**必须共用同一个打开的 fd**(比如 fork 后继承),或通过其他机制同步 fd 生命周期
- 如果用
std::fstream,得先fd = open(path, ...),再用fdopen(fd, "w")包装,否则无法控制底层 fd -
flock锁在 close(fd) 时自动释放,哪怕进程 crash 也会由内核清理——这是它比应用层标记靠谱的地方
阻塞 vs 非阻塞:用错 LOCK_NB 会直接失败
默认 flock(fd, LOCK_EX) 是阻塞的,卡住直到拿到锁。想非阻塞,必须显式加 LOCK_NB 标志,写成 flock(fd, LOCK_EX | LOCK_NB)。漏掉 LOCK_NB 还以为“没锁上”,其实是卡住了。
- 非阻塞调用失败时返回 -1,
errno == EWOULDBLOCK或EAGAIN,不是EBUSY - 不要用
usleep轮询重试——浪费 CPU,且可能错过锁释放瞬间;真要轮询,至少用poll()等待 fd 可写(虽对 flock 没用,但这是常见混淆点) -
LOCK_SH和LOCK_EX可共存(多个 SH),但 SH 和 EX 互斥;重复对同一 fd 加 EX 锁是允许的(可重入),但不同 fd 不行
跨平台?别指望 flock 在 Windows 工作
Windows 没有 flock 系统调用,MSVC 和 MinGW 都不提供兼容实现。#include <sys></sys> 在 Windows 下基本是摆设,编译直接失败。
如果你的代码需要跑 Windows,要么换方案(如命名管道、CreateFile + LockFileEx),要么用抽象层(如 Boost.Interprocess),但要注意:Boost 的 file_lock 在 Windows 底层调的是 LockFileEx,行为和 Linux flock 不完全一致(比如不继承 fork)。
- 不要写 #ifdef _WIN32 尝试 mock
flock——语义差异太大,容易埋坑 - CI 测试务必覆盖 Linux 和 Windows 两套路径,尤其检查锁是否真能互斥
- 即使只跑 Linux,也要注意容器环境(如某些 Alpine 镜像用 musl libc):
flock支持没问题,但部分精简版可能删了sys/file.h,需确认 libc 版本
flock 编译。










