flock 入門 - 多重起動を防ぐファイルロック
flock とは何か?
結論:
flockは util-linux 付属のコマンドで、ファイルを使った排他ロックによりスクリプトやジョブの多重起動を 1 行で防げる。
この記事で身につくこと:
flockで cron ジョブの多重起動を防ぐ型 が分かる- 排他 / 共有 / ノンブロッキング / タイムアウト の使い分けが分かる
- スクリプトに 自己ロックを組み込む定型句 が手に入る
結論(実務の型)
- 単発コマンドを守る →
flock -n /var/lock/job.lock cmd - スクリプト全体を守る → 先頭に
FLOCKER定型句 - 待たせたい →
-w 秒数、待たせず即失敗 →-n
前提(対象環境)
flockは util-linux に含まれ、ほぼすべての Linux ディストリに標準搭載- ロックは advisory(協調的)。
flockを使うプロセス同士でのみ効く - NFS / CIFS では実装が限定的(後述)
なぜ多重起動を防ぐ必要があるのか?
結論: バックアップやバッチが前回分の実行中に再起動すると、データ破損・二重課金・負荷暴走を招く。重なりを構造的に防ぐのが flock。
cron で 5 分おきに動かすバッチが、たまたま 5 分以内に終わらなかったとする。すると次の起動が前回の実行中に重なり、同じファイルを 2 プロセスが書き込む。結果は典型的に次のいずれか。
- 出力ファイルやログの 破損・混線
- 同じ処理の 二重実行(メール二重送信・課金二重計上)
- プロセスが積み上がり メモリ / CPU の枯渇
PID を書いたロックファイルを自前で管理する方法もあるが、プロセスが kill -9 や電源断で死ぬと stale lock(残骸) が残り、次回が永久に起動しなくなる。flock ならカーネルがプロセス終了時に自動でロックを解放するため、この掃除が要らない。
flock の基本的な使い方は?
結論: 基本形は
flock ロックファイル コマンド。ロックファイルは無ければ自動作成され、コマンド終了と同時にロックが解放される。
flock には 3 つの構文がある。
# 形1: ファイル/ディレクトリをロックしてコマンド実行 flock /var/lock/mytask.lock command args # 形2: -c でシェル経由の単一コマンドを実行 flock /var/lock/mytask.lock -c 'command1 && command2' # 形3: 既に開いたファイルディスクリプタ番号をロック flock 200
最小の例。echo を排他ロック下で実行する。
flock -x /tmp/myapp.lock echo 'running under lock'
- ロックファイル(
/tmp/myapp.lock)は存在しなければ作られる -xは 排他ロック(デフォルトなので省略可)echoが終わると同時にロックは解放される
ロックファイルは「鍵」であって「データ」ではない。中身は空でよく、削除する必要もない。ファイルが残っていても問題は起きない(ロック状態はファイルの存在ではなく、開いている fd に紐づくため)。
flock の主要オプションは?
結論: 覚えるべきは
-n(待たない)・-w(時間制限)・-s(共有)・-E(失敗時の終了コード)の 4 つ。
| オプション | 意味 |
|---|---|
-x, --exclusive |
排他ロック(書き込みロック)。デフォルト |
-s, --shared |
共有ロック(読み取りロック)。複数が同時取得可 |
-n, --nonblock |
取得できなければ待たず即失敗(終了コード 1) |
-w, --timeout 秒 |
指定秒数だけ待ち、取れなければ失敗。小数可(-w 0.5) |
-u, --unlock |
ロックを明示的に解放 |
-E, --conflict-exit-code N |
-n / -w 失敗時の終了コードを指定(既定 1) |
-o, --close |
コマンド実行前に fd を閉じる(子プロセスにロックを渡さない) |
-c, --command |
シェル(sh -c)経由で単一コマンドを実行 |
-w 0 は --nonblock と同じ意味になる(ゼロ秒待ち = 待たない)。
cron の多重起動を防ぐには?
結論: cron 行を
flock -n ロックファイルで包む。前回が実行中なら今回は黙って終了し、重なりが起きない。
これが flock の最頻出ユースケース。crontab を次のように書く。
# 5分おきにバックアップ。前回が走っていたら今回はスキップ */5 * * * * /usr/bin/flock -n /var/lock/backup.lock /opt/scripts/backup.sh
-nで 重なったら即終了(待たずにスキップ)- 前回ジョブが終わっていればロックを取得して実行
- ロックファイルは
/var/lock/か/run/lock/に置くのが慣例
「重なったら少しだけ待ってほしい」場合は -w を使う。
# 最大30秒待ち、それでも取れなければ諦める */5 * * * * /usr/bin/flock -w 30 /var/lock/backup.lock /opt/scripts/backup.sh
cron 内では flock を絶対パス(/usr/bin/flock)で書くと、PATH が最小限の環境でも確実に動く。
スクリプトに自己ロックを組み込むには?
結論: スクリプト先頭にファイルディスクリプタ方式か
FLOCKER定型句を置くと、呼び出し方を問わず自分自身を多重起動から守れる。
cron 行に毎回 flock を書く代わりに、スクリプト自身にロックを内蔵すると安全。代表的な 2 パターン。
パターン1: ファイルディスクリプタ方式
#!/bin/bash
exec 200>/var/lock/myscript.lock
flock -n 200 || { echo "already running"; exit 1; }
# --- ここから下が排他実行されるクリティカルセクション ---
echo "do work..."
sleep 10exec 200>...でロックファイルを fd 200 に開く(200 は慣例、9〜255 で空き番号なら何でもよい)flock -n 200でその fd をロック。取れなければ||側で終了- スクリプトが終わると fd が閉じ、ロックは自動解放
パターン2: FLOCKER 定型句(man 公式)
#!/bin/bash
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
# --- ここから下は必ずロック下で動く ---
echo "do work..."スクリプト自身をロックファイルとして使い、環境変数 FLOCKER で「既に flock 経由で再実行済みか」を判定する。先頭に貼るだけで自己ロックが完成する。
flock -en "$0" のように スクリプトファイル自体をロック対象にする場合、そのスクリプトを > で書き換える処理(自己更新スクリプト等)と相性が悪い。ロック対象は書き換えない専用ファイルにするのが無難。
ノンブロッキングとタイムアウトはどう使い分ける?
結論: 重なりを捨ててよいなら
-n、少し待てば処理できるなら-w 秒数。失敗を検知したいときは終了コードを見る。
# 即失敗(重なったらスキップでよいバッチ向け) flock -n /var/lock/job.lock ./job.sh # 最大10秒待つ(短時間の競合なら吸収したい処理向け) flock -w 10 /var/lock/job.lock ./job.sh
ロック取得に失敗したかどうかは 終了コードで判定する。-n / -w が失敗したときの終了コードは既定で 1、-E で変更できる。
flock -n -E 99 /var/lock/job.lock ./job.sh
if [ $? -eq 99 ]; then
echo "別プロセスが実行中のためスキップした"
fi成功してコマンドが走った場合、flock の終了コードは そのコマンドの終了コードがそのまま返る。ロック失敗(-E 値)と区別できるよう、-E には通常コマンドが返さない値を選ぶとよい。
flock のよくある落とし穴は?
結論: NFS での非対応、データファイルを
>で再生成してロックが外れる事故、ロックファイルの置き場所が主な罠。
1. NFS / CIFS では効かないことがある
flock(2) の実装はネットワークファイルシステムで限定的。マウント条件によっては flock が常に失敗、あるいはロックが効かないことがある。共有ストレージ上ではなく、ローカルの /var/lock / /run/lock にロックファイルを置くこと。
2. データファイルを再生成するとロックが外れる
ロックは「開いている fd」に紐づく。次のように出力先データファイルそのものをロックし、後で > で作り直すと inode が変わってロックが無意味になる。
# NG: data.txt をロックしつつ data.txt を再生成している flock data.txt sh -c '> data.txt; generate >> data.txt'
ロック対象は書き換えない専用ロックファイルにする。
3. flock はデッドロックを検出しない
複数のロックを交差して取り合うとデッドロックし得るが、flock 自身は検出しない。ロック取得は 1 スクリプト 1 ロックを基本にし、複数ロックを跨ぐ設計は避ける。
これらを避ければ、flock は数文字で多重起動を封じる強力で堅牢な道具になる。