锁文件比互斥量更可靠,因其不依赖OS IPC机制,仅靠文件系统原子操作(如open+O_EXCL或CreateFile+CREATE_NEW)实现跨用户、跨会话互斥,并通过PID校验与刷盘保障崩溃恢复安全性。

为什么锁文件比互斥量更可靠
Windows 上用 CreateMutex 跨用户会失败,Linux 上 POSIX 互斥量默认不跨进程持久化;而锁文件本质是文件系统原子操作,只要路径一致、权限可控,就能在任意用户/会话下生效。关键在于:锁文件不依赖 OS 进程间通信机制,只依赖文件存在性 + 打开失败判断。
常见错误是只检查文件是否存在——这有竞态:stat 返回不存在,下一毫秒另一实例就创建了它,两个进程同时进入临界区。必须用 open(..., O_CREAT | O_EXCL | O_RDWR) 原子创建并打开,失败即说明已被占用。
Linux 下用 open + O_EXCL 实现
核心是原子性创建锁文件。成功打开即获得锁,进程退出前需显式 unlink;若崩溃未清理,下次启动时旧锁文件仍存在,但可通过检查其对应 PID 是否存活来决定是否覆盖。
-
open("/tmp/myapp.lock", O_CREAT | O_EXCL | O_RDWR)必须带O_EXCL,否则无法防止竞态 - 写入当前进程 PID 到锁文件(便于后续校验),用
write()后立即fsync()确保落盘 - 程序退出前调用
unlink("/tmp/myapp.lock"),但崩溃时不会执行,所以启动时要先尝试读锁文件里的 PID,再用kill(0, pid)检查是否存活 - 不要用
/var/run(需要 root)或用户家目录(多用户环境路径不统一),/tmp是最稳妥的跨用户临时路径
Windows 下用 CreateFile 模拟相同语义
Windows 没有 O_EXCL 语义,但 CreateFile 的 CREATE_NEW 标志等价:仅当文件不存在时创建成功。失败时 GetLastError() 返回 ERROR_FILE_EXISTS。
立即学习“C++免费学习笔记(深入)”;
- 路径建议用
%TEMP%\myapp.lock,用GetEnvironmentVariable获取,避免硬编码 - 必须指定
FILE_SHARE_NONE,否则其他进程仍可打开同一文件(失去互斥意义) - 写入 PID 后调用
FlushFileBuffers(),确保内容写入磁盘 - 程序正常退出时调用
DeleteFile;异常退出后锁文件残留,同样需读取 PID 并用OpenProcess+WaitForSingleObject(超时 0)验证进程是否还活着
跨平台封装要注意的三个细节
直接写两套逻辑容易漏掉边界情况。真正难的不是创建锁,而是安全地清理和复用。
- 锁文件路径必须全局唯一且稳定:推荐拼接应用名 + 固定哈希(如 SHA256(app_name)),避免不同版本应用互相干扰
- PID 写入后必须同步刷盘(
fsync/FlushFileBuffers),否则可能锁文件存在但内容为空,导致误判 - 检查旧锁时,不能只看 PID 文件是否存在,必须验证该 PID 对应进程是否真在运行且属于本应用(Linux 可读
/proc/[pid]/comm,Windows 可查GetProcessImageFileName)
锁文件方案看似简单,但 PID 校验逻辑、路径选择、崩溃恢复这三处最容易被跳过测试,上线后在多用户或容器环境中才暴露问题。


















