适用场景
本文整理于 2026-10-22,适合需要运行报表、文件整理或本地数据处理脚本的 Linux 用户。问题往往出现在任务偶尔变慢时:上一轮还在写文件,下一轮又开始,结果出现重复记录、临时文件冲突或额外负载。
本文先建立一个只输出时间并等待二十秒的练习任务,用两个终端验证同一时刻只有一个实例运行。示例不触碰业务数据,也不修改现有定时任务。确认行为后,再将相同方法接入自己的脚本。
原理:文件是锁的载体,不是运行标志
flock 可以在执行命令前申请文件锁。默认申请排他锁,同一个锁对象在同一时刻只能由一个参与者独占。后启动的进程如果也申请这把锁,就需要等待,或者按配置立即退出。
这里的关键是所有入口都遵守同一个约定。文件锁属于协作机制,不能阻止另一个完全不申请锁的程序直接修改业务文件。因此,人工运行和计划运行都应经过同一个加锁入口。
锁由内核维护,不是根据文件中有没有文字判断。正常情况下,持有锁的相关文件描述符全部关闭后,锁便释放,磁盘上保留一个空锁文件属于正常现象。命令语法与选项见 util-linux 官方手册。
准备工作
准备两个使用同一普通用户的终端,以及 Bash 和 util-linux 提供的 flock 工具。将实验目录放在本机文件系统上,避免一开始就涉及网络文件系统的锁语义。
先确认命令存在:
command -v flock
flock --version
如果找不到命令,应通过当前发行版的官方软件仓库安装对应软件包。本文不要求使用管理员权限,也不建议为了方便把锁文件放进所有人都能随意替换的公共目录。
分步骤操作
第一步:创建独立练习目录
在第一个终端执行:
lab_dir=$(mktemp -d "$HOME/flock-lab.XXXXXX")
chmod 700 "$lab_dir"
printf '%s\n' "$lab_dir"
cd "$lab_dir"
lab_dir 保存本次新建目录的实际路径,末尾随机字符由系统生成。记下打印出的完整路径,第二个终端需要进入同一目录。这里的 $HOME 使用当前用户原有的主目录变量,不需要改写。
第二步:创建可观察的任务
继续执行以下代码生成练习脚本:
cat > job.sh <<'EOF'
#!/bin/bash
printf '开始:'
date --iso-8601=seconds
sleep 20
printf '完成:'
date --iso-8601=seconds
EOF
chmod 700 job.sh
脚本始终在前台运行,打印开始时间,等待二十秒,再打印完成时间。等待只是为了留下并发观察窗口;实际使用时,应将任务主体换成已有并经过验证的业务操作。
第三步:在第一个终端取得锁
执行:
flock -n -E 75 "$PWD/job.lock" bash "$PWD/job.sh"
job_status=$?
printf '退出状态:%s\n' "$job_status"
$PWD 是当前目录的绝对路径。-n 表示拿不到锁就立即退出,避免任务排队堆积;-E 75 将锁冲突的退出状态设为 75。第一个终端应显示开始时间,并等待任务结束。
第四步:在第二个终端争用同一把锁
趁第一个任务仍在等待,打开第二个终端执行:
read -r -p '请输入第一终端打印的实验目录完整路径:' lab_dir
cd "$lab_dir"
flock -n -E 75 "$PWD/job.lock" bash "$PWD/job.sh"
job_status=$?
printf '退出状态:%s\n' "$job_status"
输入的是本次实验目录的真实路径。第二次执行应迅速返回,不打印任务的开始时间,退出状态为 75。若已经超过二十秒,第一个任务可能释放了锁,此时第二次能够运行属于正常结果。
验证方法
等待第一个任务结束,确认它打印完成时间且退出状态为 0。随后在第二个终端再次执行相同加锁命令,应能正常启动。这一步证明锁会随任务结束释放,无需删除锁文件。
还可以在第一个终端运行期间按 Ctrl+C 中止练习,再尝试第二次执行,观察是否能够取得锁。对于真实任务,中止前应先了解它是否支持安全取消;不能把练习中的等待程序等同于正在写数据库的生产程序。
常见错误
两个入口使用不同目录中的同名锁文件,实际上不会互斥。使用相对路径时尤其容易出现这种情况,因此接入正式任务应使用固定绝对路径,并让所有入口引用同一文件。
另一个错误是看到锁文件存在就删除它。运行中的进程可能仍锁着旧文件,新进程却对重新创建的文件加锁,反而出现两个实例同时运行。锁文件是否存在不能说明锁是否被占用。
退出状态也需要区分:任务成功启动后,flock 通常返回任务本身的退出状态。正式脚本如果也使用 75 表示自身错误,就需要调整约定并配合日志判断,不能把所有非零状态都当成正常跳过。
安全与回退
先将加锁入口用于人工运行,再替换计划任务的入口,保留原配置以便恢复。需要每轮必达的业务,不应简单采用冲突即跳过的策略,应另行设计排队或补跑机制。
任务应保持前台执行;如果脚本启动后台进程,文件描述符继承可能影响锁的释放时间。网络文件系统也可能具有不同限制,相关边界见 Linux flock 系统调用说明。
回退时,先停止新增触发并等待现有任务结束,再恢复原入口。实验清理只需删除此次独立目录中的脚本和锁文件,然后移除空目录;不要在仍有任务运行时删除锁文件。
参考资料与转载说明
本文依据上述公开资料进行中文原创整理,不是逐句翻译,也不替代厂商手册。操作前请核对自己的版本、型号和备份。
评论一下?