根本原因是fcntl要求文件描述符必须可写(O_RDWR/O_WRONLY),只读fd调用F_SETLK会返回-1并设EBADF;此外需注意偏移重置、伪文件限制、Windows与Linux锁语义差异、避免flock混用、错误分类重试及继承性差异。

Linux/macOS 下用 fcntl 加锁为什么总失败?
根本原因通常是没开 O_RDWR 或 O_WRONLY,fcntl 的 F_SETLK 对只读 fd 会直接返回 -1 并设 errno = EBADF。Windows 的 LockFile 倒不挑打开方式,但跨平台封装时容易统一错。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 所有文件锁前必须用
open(..., O_RDWR | O_CREAT)(或至少带写权限),哪怕你只读也得开写——fcntl不认“只读锁”这回事 - 加锁前先
lseek(fd, 0, SEEK_SET),避免因文件偏移导致锁区域错位(尤其多次read/write后) - 别对
/proc、/sys下的伪文件或管道用fcntl,会返回ENOLCK或静默失败
Windows LockFile 和 fcntl 的偏移单位不一致?
是的,LockFile 的 dwFileOffsetLow/dwFileOffsetHigh 是字节偏移,和 fcntl 的 l_start 表面一致,但关键差异在:Windows 锁的是“从该偏移开始的连续字节”,而 fcntl 的 l_len 为 0 表示“锁到文件末尾”,Windows 没这功能,必须显式传长度。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 封装时统一用“起始偏移 + 长度”语义,别依赖
fcntl的l_len == 0特性 - 若需锁整个文件,Linux/macOS 传
l_len = 0,Windows 则要先GetFileSize再传实际大小——否则可能只锁前 4GB(DWORD溢出) -
LockFileEx支持共享锁(LOCKFILE_EXCLUSIVE_LOCK = 0),但fcntl的F_RDLCK在多数文件系统上只是 advisory,别指望它拦住 mmap 写入
跨平台封装时怎么避免 flock 和 fcntl 混用?
flock 是 BSD 风格的文件描述符锁,和 fcntl 锁互不感知,同一文件上混用会导致行为不可预测——比如 flock 加了锁,fcntl 还能成功加,反之亦然。Windows 根本没有 flock,所以封装层必须彻底屏蔽它。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 头文件里直接
#undef _GNU_SOURCE或不用flock相关声明,防止误调 - CMake 或构建脚本中加检查:
check_symbol_exists(fcntl "fcntl.h" HAVE_FCNTL),没fcntl就禁用 Unix 路径 - 封装类的构造函数里,Linux/macOS 强制走
fcntl路径,Windows 强制走LockFileEx,绝不 fallback 到flock
锁失败后重试逻辑该不该加?
该加,但不能无脑 usleep(1000)。Linux 下 fcntl 返回 EAGAIN 或 EACCES 表示冲突,Windows LockFileEx 返回 FALSE 且 GetLastError() == ERROR_LOCK_VIOLATION 才算真冲突;其他错误(如 INVALID_HANDLE_VALUE)是使用错误,重试只会恶化。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 只对明确的锁冲突错误重试,其他错误直接抛异常或返回失败码
- 重试最多 3–5 次,间隔用指数退避(1ms → 2ms → 4ms),避免线程忙等耗光 CPU
- 务必设超时时间(比如总耗时 ≤ 100ms),防止死锁场景下无限卡住



















