← 中文资源

简体中文版本

Flock 文件锁互斥临界区

用 flock 在锁文件上实现互斥:非阻塞抢锁拿不到即退出防重复执行,锁绑定文件描述符随进程退出自动释放。

阅读英文原文

示例说明

cron 里每小时跑的同步任务,上一次卡住没退出时下一次又启动——双实例同时写就是事故。flock 的锁绑定文件描述符而非文件存在性:进程退出(包括被杀)时内核自动释放锁,不存在残留死锁;-n 非阻塞模式让后来者立即知难而退,调度语义就是跳过本次而非排队。

技术要点

  • 锁随进程退出由内核自动释放
  • -n 非阻塞语义即跳过不排队
  • 描述符与锁生命周期绑定

代码解读

exec 9> 打开锁文件并占用描述符九,flock -n 9 抢锁失败即走跳过分支——exit 0 让 cron 不报错,跳过是正常调度结果;critical_section 里即使异常退出,描述符关闭即锁释放。结束时显式关闭描述符,防止本进程派生的后台任务意外继承锁。

使用建议

锁文件路径要稳定且属专用目录。等待模式 -w 配超时防长队。锁内逻辑仍要幂等——锁挡并发不挡崩溃后重跑。

代码示例

#!/usr/bin/env bash
set -euo pipefail

readonly LOCK_FILE="/tmp/nightly-sync.lock"
readonly LOCK_WAIT=5

# 形态一:非阻塞抢锁,拿不到直接退出(调度器友好)
exec 9>"${LOCK_FILE}"
if ! flock -n 9; then
  echo "另一实例正在运行,跳过本次执行" >&2
  exit 0
fi

# 形态二:限时等待锁(需要排队的场景)
# flock -w "${LOCK_WAIT}" 9 || { echo "锁等待超时" >&2; exit 75; }

echo "获得锁,PID $$ 开始同步"

critical_section() {
  local started=$(date +%s)
  # ... 同步主体,异常退出时锁随文件描述符自动释放 ...
  sleep 2
  echo "同步完成,耗时 $(( $(date +%s) - started ))s"
}

critical_section

# 拿到锁后派生的后台任务也持锁,需要先关描述符
exec 9>&-
echo "锁已释放,脚本结束"