linux/macos下必须用os.o_rdwr打开文件才能加写锁,因内核要求lock_ex需可写fd;只读打开在部分内核会拒绝加锁或静默失败,且锁绑定fd而非路径,非阻塞需显式加lock_nb。

Linux/macOS 下必须用 os.O_RDWR 打开文件才能加写锁
很多人写 os.OpenFile("data.txt", os.O_RDONLY, 0644) 后直接调 syscall.Flock,结果报 EINVAL 或静默失败。这不是 Go 的 bug,是内核对 flock(2) 的硬性要求:写锁(LOCK_EX)必须在可写的 fd 上申请。
- 只读打开的 fd 在部分 Linux 内核(如 5.10+)和 macOS 上会拒绝
LOCK_EX,但可能允许LOCK_SH -
os.O_CREATE | os.O_RDWR是安全起点;os.O_WRONLY也行,但后续无法读取锁文件内容(比如检查 pid) - 别依赖
os.O_APPEND—— 它不改变 fd 的读写权限位,不影响锁能力
非阻塞加锁必须显式带 LOCK_NB 标志
syscall.Flock(fd, syscall.LOCK_EX) 默认是阻塞的,goroutine 会卡死直到锁释放。生产环境几乎从不这么用。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 正确写法是
syscall.Flock(fd, syscall.LOCK_EX|syscall.LOCK_NB),失败时返回syscall.EWOULDBLOCK或syscall.EAGAIN - 别用
syscall.F_SETLK混搭 ——syscall.Flock和syscall.FcntlFlock是两套系统调用,混用可能导致状态不一致 - 超时重试逻辑要自己写,标准库不提供;常见模式是循环 +
time.Afterselect
Windows 上 syscall.Flock 直接不可用,必须换方案
运行时报 function not implemented 或编译期报 undefined: syscall.Flock,不是你代码错了,是 Windows 根本没实现 POSIX flock(2)。
- Go 1.19+ 的
*os.File.Lock()在 Windows 上底层调LockFileEx,但它在 Linux/macOS 上会 panic —— 别跨平台用 - 可靠方案只有两个:
golang.org/x/sys/windows.CreateFile+LockFileEx(需传dwShareMode = 0),或第三方包如github.com/gofrs/flock - 用
gofrs/flock时务必确认版本 ≥ 0.8.1 并启用flock.WithExclusiveCreate(),否则两个进程可能同时创建锁文件,导致“假成功”
锁绑定的是 fd,不是文件路径
同一个文件被 os.OpenFile 两次,得到两个独立 fd,各自加的锁互不影响。这是最常被忽略的底层事实。
- 框架里常见错误:
os.OpenFile→ 传给锁函数 → 函数内部又os.OpenFile一次 → 对新 fd 加锁 → 原 fd 没锁 → 并发照旧 - 锁的生命周期与 fd 绑定:进程退出时内核自动释放,但显式调
syscall.Flock(fd, syscall.LOCK_UN)更利于调试和资源追踪 - fork 子进程会复制 fd,锁状态也被继承;若子进程 exit,父进程 fd 上的锁依然有效
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










