web-dev-qa-db-ja.com

bashの `wait`コマンド、2つ以上のPIDが実行を完了するのを待つ

最近、PIDの再利用を 防ぐことが可能であるかどうかを尋ねる質問を投稿しました

これまでのところ、答えはノーのようです。 (どちらでもかまいません。)

しかし、ユーザー Diego Torres Milano がその質問に回答を追加しました。ここでの私の質問は、その回答に関するものです。

ディエゴは答えた、

PIDを再利用するのが怖い場合は、他の回答で説明されているように待てば発生しませんが、

echo 4194303 > /proc/sys/kernel/pid_max

あなたの恐怖を減らすために;-)

ここでDiegoが4194303という数字を使用した理由は実際にはわかりませんが、それは別の質問です。

私の理解は、次のコードに問題があったことです:

for pid in "${PIDS[@]}"
do
    wait $pid
done

問題は、配列に複数のPIDがあり、forループが配列の各PIDでwaitコマンドを順番に実行することですが、プロセスが同じ順序で完了すると予測できません。 PIDはこの配列に格納されます。

すなわち;次のが発生する可能性があります

  • 配列インデックス0のPIDの待機を開始します
  • 配列のインデックス1にPIDがあるプロセスは終了します
  • 新しいジョブがシステムで実行され、PID配列のインデックス1に格納されているPIDが別のプロセスで再利用されます
  • waitは、配列インデックス0のPIDが終了すると終了します
  • 配列インデックス0でPIDの待機を開始します。ただし、これは現在別のプロセスであり、それが何であるかがわかりません
  • waitが現在待っているneverPIDを再利用して実行されたプロセスは終了します。おそらくそれは、システム管理者が開始したメールサーバーまたは何かのPIDです。
  • waitは、次に深刻なLinuxバグが見つかり、システムが再起動されるか、停電になるまで待機し続けます

ディエゴは言った:

他の答えが説明するようにあなたが待つならば、それは起こりません

すなわち;私が上で述べた状況は起こり得ないということです。

ディエゴは正しいですか?

  • もしそうなら、なぜ私が上で述べた状況が起こらないのですか?

または、ディエゴは正しくありませんか?

  • もしそうなら、私は今日後で新しい質問を投稿します...

その他の注意事項

PIDがバックグラウンドで起動されたプロセスのPIDであることを認識していない限り、この質問は混乱を招く可能性があることに気付きました。すなわち;

my_function &
PID="$!"
PIDS+=($PID)
8
user3728501

オプションを見てみましょう。

すべてのバックグラウンドジョブを無条件に待つ

for i in 1 2 3 4 5; do
    cmd &
done
wait

これは単純であるという利点がありますが、マシンをビジー状態に保つことはできません。古いジョブが完了したときに新しいジョブを開始したい場合は、できません。すべてのバックグラウンドジョブが完了するまで、マシンの使用率は低下します。その時点で、ジョブの新しいバッチを開始できます。

関連するのは、waitに複数の引数を渡して、ジョブのサブセットを待機する機能です。

unrelated_job &
for i in 1 2 3 4 5; do
  cmd & pids+=($!)
done
wait "${pids[@]}"   # Does not wait for unrelated_job, though

任意の順序で個々のジョブを待つ

for i in 1 2 3 4 5; do
   cmd & pids+=($!)
done

for pid in "${pids[@]}"; do
   wait "$pid"
   # do something when a job completes
done

これには、ジョブが完了した後に作業を行えるという利点がありますが、ジョブotherよりも問題があります$pidが最初に完了し、$pidは実際に完了します。ただし、実際に待機する前に完了した場合でも、個々のジョブの終了ステータスを取得します。

nextジョブが完了するのを待ちます(bash 4.3以降)

for i in 1 2 3 4 5; do
   cmd & pids+=($!)
done

for pid in "${pids[@]}"; do
   wait -n
   # do something when a job completes
done

ここでは、aジョブが完了するまで待機できます。つまり、マシンをできるだけビジー状態に保つことができます。唯一の問題は、アクティブなプロセスのリストを取得するためにjobsを使用してそれをpidsと比較することなく、必ずしも完了したwhichジョブがわかっていないことです。

別のオプション?

シェル自体はジョブ分散を行うための理想的なプラットフォームではありません。そのため、バッチジョブを管理するために設計された多数のプログラムがあります:xargsparallelslurmqsubなど.

27
chepner

これは古いですが、pidの衝突が原因で遅延waitがランダムな無関係のプロセスを待機するというシナリオは、直接対処されていません。

カーネルレベルでは不可能です。親プロセスがwait(2)¹を呼び出す前に、子プロセスまだ存在するが機能します。子がまだ存在しているため、Linuxは再利用するのではなくPIDを使い果たします。これは、いわゆるゾンビプロセスまたは「デファクト」プロセスで時々現れます。これらは、終了したが、親によってまだ「取得」されていない子です。

シェルレベルでは、子プロセスを取得するためにwait(1)¹を呼び出す必要はありません-bashがこれを自動的に行います。私は確認していませんが、ずっと前に終了した子pidに対してwait $pidを実行すると、bashは、その子をすでに取得していて、何も待たずにすぐに情報を返すことに気づきます。

_ wait(N)表記は、APIレイヤーを明確にするために使用される規則です-Nは、コマンド/関数が配置されているマニュアルのセクションを指します。この場合、次のようになります。

  • wait(2):syscall-man 2 waitを参照
  • wait(1):シェルコマンド-man 1 waitまたはhelp waitを参照

各マニュアルセクションの内容を知りたい場合は、man N introを試してください。

0
sqweek