定時任務(cron)執行的備份/同步腳本若上次未結束就觸發下次,會導致數據衝突、資源耗盡。本文從簡單鎖文件演進到原子操作 noclobber + trap 信號清理的健壯方案,並分析邊界情況。
LOCK="/tmp/my.lock"
[[ -e $LOCK ]] && { echo "re-entry, exiting"; exit 1; }
touch $LOCK
# 正常業務邏輯
rm -f $LOCK
缺陷:[[ -e ]] 和 touch 非原子,兩進程同時通過檢查;腳本異常退出遺留鎖文件導致永久阻塞。
LOCK="/tmp/my.lock"
if ( set -o noclobber; echo "$$" > "$LOCK" ) 2>/dev/null; then
trap 'rm -f "$LOCK"; exit $?' INT TERM EXIT
# 正常業務邏輯
rm -f "$LOCK"
trap - INT TERM EXIT
else
echo "Failed to acquire lock: $LOCK. Held by $(cat $LOCK)"
exit 1
fi
| 技術 | 作用 |
|---|---|
set -o noclobber | 禁止 > 覆蓋現有文件,保證 echo $$ > lock 原子性 |
$$ | 當前進程 PID,寫入鎖文件便於排查持有者 |
trap 'cmd' SIG | 捕獲信號執行清理:INT(Ctrl+C)、TERM(kill)、EXIT(正常退出) |
trap - SIG | 恢復信號默認處理,避免影響子進程 |
Ctrl+C 中斷腳本kill -TERM PID 優雅停止exit、執行完畢)kill -9 (SIGKILL) 內核直接終止,無法執行 trap/tmp 通常清空,鎖自動釋放flock 或 fcntl 鎖,普通文件鎖不可靠/tmp/my_${param_hash}.lock| 方案 | 優點 | 缺點 |
|---|---|---|
flock -n | 內核級鎖、自動釋放、支持共享/排他 | 需安裝 util-linux、不支持 NFS |
mkdir lockdir | 目錄創建原子性、跨平台 | 需手動清理、無進程信息 |
PID 文件 + kill -0 | 可檢測進程存活 | 競態條件複雜、需防 PID 復用 |