Linux/macOS用flock(fd, LOCK_SH|LOCK_NB)检测,失败且errno==EWOULDBLOCK说明被独占锁定;Windows用CreateFile()配FILE_SHARE_READ,失败且GetLastError()==ERROR_SHARING_VIOLATION说明被写打开;stat()等方法不可靠。

用 flock() 检测文件是否被其他进程加锁(Linux/macOS)
Linux 和 macOS 下最直接的方式是尝试对文件描述符加一个非阻塞共享锁。如果失败且 errno == EWOULDBLOCK,说明文件正被另一个进程以排他方式锁定(比如正在写入)。注意:flock() 锁是建议性锁,不强制生效,但绝大多数标准工具(如 cp、rsync、日志轮转脚本)会主动配合它。
实操要点:
- 必须先
open()文件获取有效 fd,不能对路径直接调用flock() - 用
LOCK_SH | LOCK_NB尝试加共享锁——写入进程通常持LOCK_EX,冲突即报错 - 若
flock(fd, LOCK_SH | LOCK_NB) == -1且errno == EWOULDBLOCK,基本可判定被写入锁定 - 别忘了
close(fd),否则可能干扰后续操作;fd 不必保持打开状态来维持检测结果
int fd = open("/path/to/file", O_RDONLY);
if (fd == -1) { /* 处理 open 失败 */ }
if (flock(fd, LOCK_SH | LOCK_NB) == -1) {
if (errno == EWOULDBLOCK) {
// 被其他进程独占锁定,很可能正在写入
}
}
close(fd);Windows 上用 CreateFile() 判断写入占用(非重叠 I/O)
Windows 没有 flock(),得靠打开文件时的共享模式判断。核心逻辑:以 GENERIC_READ 打开,同时声明不希望别人写——即传 FILE_SHARE_READ(不带 FILE_SHARE_WRITE)。如果失败且 GetLastError() == ERROR_SHARING_VIOLATION,说明当前有进程以写权限打开了该文件。
关键细节:
立即学习“C++免费学习笔记(深入)”;
- 必须用
OPEN_EXISTING,不能用CREATE_ALWAYS或TRUNCATE_EXISTING,否则行为不可控 - 即使只是读取目的,也别设
FILE_SHARE_WRITE,否则永远检测不到写入占用 - 某些程序(如记事本)写入时并不长期持有句柄,而是“打开→写→关闭”,这种瞬时占用容易漏判
-
CreateFile()成功返回句柄后务必CloseHandle(),否则可能触发资源泄漏或后续打开失败
HANDLE h = CreateFileA("C:\path\to\file",
GENERIC_READ, FILE_SHARE_READ, // 关键:不共享写
NULL, OPEN_EXISTING, 0, NULL);
if (h == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
if (err == ERROR_SHARING_VIOLATION) {
// 很可能被其他进程以写方式打开中
}
}
if (h != INVALID_HANDLE_VALUE) CloseHandle(h);为什么 stat() 或修改时间对比不可靠
很多人想通过反复调用 stat() 看 st_mtime 是否变化来判断“是否正在写入”,这完全无效。
-
st_mtime只在写入完成并close()后才更新,写入过程中它不变 - 有些程序(如数据库、日志库)写入时先写临时文件再原子
rename(),原文件mtime根本不动 - 文件系统缓存、NFS 延迟、甚至
touch命令都会污染mtime,无法区分“真写入”和“假动静” - 只读挂载、只读属性、ACL 限制等场景下,
stat()返回值和实际可写性完全脱钩
跨平台检测的现实妥协点
没有 100% 可靠的跨平台方案。Linux/macOS 依赖 flock(),Windows 依赖 CreateFile() 共享标志,二者语义不等价,且都受程序自身是否配合影响。
- 不要试图封装成统一函数名对外隐藏差异——调用方必须清楚自己在哪种系统上运行
- 若目标文件由第三方程序控制(如 nginx 日志、MySQL binlog),优先查其文档是否提供锁定机制或状态接口
- 检测结果只能作为“大概率提示”,不能替代业务层的重试、队列或信号通知机制
- 最易忽略的一点:检测本身有竞态窗口。哪怕刚判定“未被锁定”,下一微秒就可能被别的进程抢占——所有检测都只是快照



















