waitコマンドとLinux/Unixの仕様メモ

バックグラウンドプロセス2個以上をwaitして実行結果を取得する際のメモです。Linux(RHEL7、CentOS7)で確認済みですが、Unixでも同じことが言えるはずです。

まずは間違っている例です。下記の様に、プロセスIDを取得して、waitする際に2個のプロセスIDを指定してしまうと、誤った結果が得られます。(最後に指定したプロセスIDの結果のみが取得できる。)

$ cat parent.sh
#!/bin/sh

./child.sh $1 &
child1p=$!

./child.sh $2 &
child2p=$!

sleep 3

wait $child1p $child2p
childr=$?

echo "child1p:$child1p, child2p:$child2p, childr:$childr"
$ cat child.sh
#!/bin/sh
sleep 1
exit $1

下記が実行結果です。

$ time ./parent.sh 1 1
child1p:10159, child2p:10160, childr:1

real    0m3.005s
user    0m0.005s
sys     0m0.003s
$ time ./parent.sh 0 0
child1p:10165, child2p:10166, childr:0

real    0m3.005s
user    0m0.004s
sys     0m0.003s
$ time ./parent.sh 0 1
child1p:10171, child2p:10172, childr:1

real    0m3.005s
user    0m0.001s
sys     0m0.006s

うまくいくやん! と、思われ方。要注意! 下記を実行すれば誤りに気付きます。

$ time ./parent.sh 1 0
child1p:10152, child2p:10153, childr:0

real    0m3.006s
user    0m0.004s
sys     0m0.004s

引数pidを指定した場合,waitコマンドは最後の完了を待ったプロセスのコマンドの終了コードで終了します。
http://itdoc.hitachi.co.jp/manuals/3021/3021313320/JPAS0399.HTM

正しい実装は下記。それぞれ個別にwaitする必要がある。

$ cat parent2.sh
#!/bin/sh

./child.sh 0 &
child1p=$!

./child.sh 1 &
child2p=$!

sleep 3

wait $child1p
child1r=$?

wait $child2p
child2r=$?

echo "child1p:$child1p, child2p:$child2p, child1r:$child1r, child2r:$child2r"

ん? と思われた方。そう、子プロセスが[sleep 1]しているにも関わらず、親プロセスで[sleep 3]しているため、waitにたどり着くまでに子プロセスは終了しています。「終了したプロセスの実行結果なんて取得できるの?」と。

waitコマンドについて詳しく言及した記事はありませんでしたが、waitシステムコールについて調べると、ありました。

終了したが、wait されていない子プロセスは「ゾンビ」になる。後で親プロセスが wait を実行して子プロセスについての情報を取得できるように、カーネルはゾンビプロセスについて最小限の情報 (PID、終了ステータス、 リソース使用状況) を保持する。ゾンビプロセスは、 wait によってシステムから削除されない限り、カーネルのプロセステーブルの 1 エントリーを消費する。このプロセステーブルが 一杯になると、新たにプロセスを作ることができなくなる。親プロセスが終了すると、その親プロセスの「ゾンビ」の子プロセスは (もしあれば) init(1) の養子となる。 init(1) は wait を自動的に実行し、ゾンビを削除する。
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/wait.2.html

「養子」という表現、かなり好きです。(そこではない) つまり、子プロセスの実行結果は、親プロセスが終了するまでは間違いなく保持されるようです。そのため、上記コードは問題なしです。

$ time ./parent2.sh 1 0
child1p:10185, child2p:10186, child1r:0, child2r:1

real    0m3.006s
user    0m0.004s
sys     0m0.004s

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください