pid文件+flock互斥的核心是用flock($fp, lock_ex|lock_nb)原子性加锁并写pid,避免竞态;仅检查pid文件存在性会导致双重启动或残留误判,而flock天然保证操作不可分割。

用 PID 文件 + flock 实现可靠进程互斥
核心思路是:脚本启动时,先尝试对一个固定路径的锁文件(如 /tmp/myapp.pid)加独占非阻塞锁;加锁成功则写入当前进程 PID 并继续执行,失败则直接退出。这比单纯检查文件是否存在更安全,避免竞态条件。
关键代码结构(推荐写法)
以下为生产环境可用的最小可靠实现:
- 使用 fopen(..., 'c') 打开或创建锁文件,确保文件存在且不被清空
- 用 flock($fp, LOCK_EX | LOCK_NB) 尝试非阻塞加锁——失败即说明已有实例在运行
- 加锁成功后,调用 ftruncate($fp, 0) 清空旧 PID,再 fwrite($fp, getmypid()) 写入新 PID
- 全程包裹在 try/finally 中,确保无论正常退出还是异常中断,都能 flock($fp, LOCK_UN) 释放锁并 fclose($fp)
为什么不能只靠判断 PID 文件是否存在?
仅检查 /tmp/myapp.pid 是否存在再读取 PID、再用 kill -0 验证进程是否存活,存在明显漏洞:
- 两个进程几乎同时判断文件不存在 → 都创建文件 → 都写入 PID → 双重启动
- PID 文件残留(比如上次崩溃未清理),但对应进程早已退出,此时误判为“正在运行”而拒绝启动
- 验证进程存活需额外系统调用,增加延迟和不确定性
flock 的原子性天然规避了这些问题——加锁与写 PID 是一个不可分割的操作。
守护进程场景下的补充建议
若脚本以守护进程方式长期运行(如通过 nohup php daemon.php & 或 systemd 启动),还需注意:
- 锁文件路径必须用绝对路径,避免因工作目录变化导致锁失效
- 不要依赖进程退出时 OS 自动关闭文件句柄来释放锁——某些情况下(如 SIGKILL)可能来不及释放,应显式调用 flock(..., LOCK_UN)
- 可在主循环中定期调用 posix_kill(getmypid(), 0) 自检,发现自身被意外终止时主动清理锁并退出
- 配合 systemd 使用时,建议配置 StartLimitIntervalSec=60 和 StartLimitBurst=3,防止频繁崩溃重启干扰锁逻辑
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











