バックグラウンドプロセス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