Linuxでの古いファイルロックの処理とクラスタの強力な使用

Linuxでの古いファイルロックの処理とクラスタの強力な使用

cronを介して定期的に(数分ごとに)実行されるスクリプトがあります。しかし、スクリプトは何度も並列に実行されず、時には少し時間がかかるので、いくつかのロックを実装したいと思います。つまり、以前のインスタンスがすでに実行されている場合は、スクリプトが早く終了していることを確認してください。

さまざまな提案に基づいて、次のロックが作成されました。

lock="/run/$(basename "$0").lock"
exec {fd}<>"$lock"
flock -n $fd || exit 1

スクリプトの他のインスタンスがまだ実行されている場合は、終了1を呼び出す必要があります。

これで問題は、スクリプトが終了した後も古いロックが残っていることがあることです。これは事実上、cronが再度実行されないことを意味します(次の再起動まで、またはロックされたファイルが削除されるまで)。これは確かに私が望むものではありません。

lslocksコマンドが既存のファイルロックを一覧表示できることがわかりました。これは次のことを示しています。

(unknown)        2732 FLOCK        WRITE 0     0   0 /run...                                                                 

プロセス(この場合は2732)はもう存在しません(例:ps aux)。完全なファイル名(例:/ run ...)が表示されない理由もわかりません。 lslocksには--notrucate引数がありますが、これはファイル名が切り捨てられるのを避けることができるように聞こえますが、出力は変更されず、まだ/ run ...です。

だから、いくつかの質問があります。

  • これらのロックがあるのはなぜですか、クラスタのロックがプロセスのライフサイクルの外側に存在するのはなぜですか?
  • lslocksがフルパス/ファイル名を表示しないのはなぜですか?
  • これを防ぎ、スクリプトロックをより強力にするための良い方法はありますか?
  • 再起動せずに古いロックをクリーンアップする方法はありますか?

答え1

ロックはflockファイル記述オブジェクトに関連付けられており、ファイル記述を参照するすべてのファイル記述子が閉じられると消えます。foll.2 マンページ)。

ファイルがまだロックされている場合、ファイルディスクリプタは元のプロセスまたはサブプロセスでまだ参照されることがほとんどあります(元のプロセス層の外に参照を伝播するために渡すファイルディスクリプタなどを使用しないと仮定します)。

私はそれを確認することをお勧めしますsudo fuser $lock_path

この問題を解決するために、私は2つの方法を知っています。つまり、シェルが子プロセスがファイルディスクリプタを継承しないようにするか、ファイルディスクリプタを参照しているすべてのプロセスをまだ終了することですfuser -k ...

表示されるパスは情報を収集するlslocksために使用されるため、不完全です/proc/locks。ファイルにはマウントポイントの識別子とロックを取得したプロセスに関する情報が含まれていますが、ロックファイルへのパスは含まれていません。プロセスの確認時にロックを保持するファイル記述子が見つからない場合は、lslocksマウントポイントの印刷に戻ります。

答え2

私の群れにも同じ問題がありました。 fusionの使用に関するthejhの提案が問題を見つけるのに役立ちました。クラスタで実行されたコマンドがバックグラウンドに残っているサブプロセスを開始したことがわかりました。したがって、元のコマンドが完了してもサブプロセスはロックを保持しているため、Flockはファイルのロックを解除しません。

解決策:群れ - 閉じる

「manflock」は、--closeが「コマンドを実行する前にロックを保持するファイル記述子を閉じます。これは、コマンドがロックを維持してはならない子プロセスを生成する場合に便利です」と言います。

これは私の問題を完全に解決しました。

答え3

これで、スクリプトが一度だけ実行されるようにするまったく異なる方法でこの問題を解決しました。これは私の元の質問に対する答えではありませんが、他の人に役立つようにここで共有します。

私はpgrepを使って同じ名前で実行されているプロセスの数を確認しています。この可能性はTwitterで私に指摘されました。私の考えでは、このアプローチの唯一の欠点は、同じ名前の複数のスクリプトがあると邪魔になる可能性があることです。ただし、これは十分に具体的なスクリプト名を使用することで回避できます。

これは私が使用するコードです:

PNAME="$(basename "$0")"
if [[ "$(pgrep -c -u $USER $PNAME )" -ne 1 ]]; then
    exit 1
fi

関連情報