flock -n 的“错失即放弃”是设计目标而非缺陷,加锁失败立即退出(退出码1),配合-x实现互斥、内核自动释放锁,crontab中用单引号包裹命令并记录skipped日志,避免顺延导致的阻塞与积压。

不需要顺延,flock 的非阻塞模式(-n)本质就是“错失即放弃”——它不排队、不等待、不重试,触发时抢锁失败就立刻退出,完全符合“自动放弃时间窗口”的要求。
核心逻辑:放弃不是缺陷,而是设计目标
flock -n 的行为就是:当前任务若正在运行,新调度到来时不做任何延迟或排队,直接跳过。这恰恰避免了任务堆积、资源抢占和状态混乱,是应对不确定执行时长(如网络请求、大文件备份)最干净的策略。
- -n(nonblock) 是关键开关:加锁失败立即返回,退出码为 1,crontab 不会重试也不会等待
- -x(exclusive) 确保互斥:同一时间只允许一个实例持有该锁
- 锁文件生命周期由内核管理:进程退出(无论正常结束、kill 还是崩溃),锁自动释放,无残留风险
crontab 中的标准写法
在 crontab 条目中直接包裹命令,不依赖脚本内部逻辑:
* * * * * flock -xn /var/run/backup.lock -c '/usr/local/bin/backup.sh >> /var/log/backup.log 2>&1' || echo "$(date): skipped — lock held" >> /var/log/backup.log
- 使用 /var/run/ 而非 /tmp/:避免系统自动清理导致锁文件丢失、误判为“空闲”
- 单引号包裹整个命令串:防止 shell 解析错误,尤其含重定向或管道时
- || 日志反馈:明确记录“被跳过”,区分“未触发”和“被锁拦截”,便于监控排查
为什么不能也不该“顺延”
所谓“顺延”,比如等前一个结束再执行、或积压后批量执行,会违背定时任务的原始语义,并引入新问题:
- 等待 = 阻塞 cron worker,可能拖慢其他任务,且无法预估等待时长
- 积压 = 进程队列膨胀,内存/CPU 占用不可控,异常时更难清理
- 业务上多数场景(如部署、备份、通知)并不要求“绝对准时”,而要求“安全不并发”——错过一次,下次周期再试,更稳健
辅助确认与排障方式
快速验证是否按预期放弃,而非意外失效:
- 查日志:
tail -f /var/log/backup.log看是否有 “skipped” 记录 - 查锁状态:
lsof /var/run/backup.lock或ps aux | grep backup.sh - 手动测试:运行
flock -xn /var/run/backup.lock -c 'sleep 10'后立刻再执行一次,应立即失败并退出










