シェルが覚えるべきプロセス ID
ほとんどの POSIX シェルは、パイプラインを構成するそれぞれのコマンドをシェルの直接の子プロセスとして実行する。Yash 2 もさうなってゐる。が、実は必ずしもさうしなくてよい。
今の yash-2.52 で yash -c 'ps f | cat - | head& wait $!'
の様なコマンドを実行すると以下の様なプロセスツリーになる。
142461 pts/0 S+ 0:00 \_ yash -c ps f | cat - | head& wait $! 142462 pts/0 R+ 0:00 \_ ps f 142463 pts/0 S+ 0:00 \_ cat - 142464 pts/0 S+ 0:00 \_ head
一方、今の yash-rs (コミット 00577c95) で同様にすると以下の様になる。
143312 pts/0 S+ 0:00 \_ target/debug/yash 143371 pts/0 S+ 0:00 \_ target/debug/yash 143372 pts/0 S+ 0:00 \_ target/debug/yash 143375 pts/0 R+ 0:00 | \_ ps f 143373 pts/0 S+ 0:00 \_ target/debug/yash 143376 pts/0 S+ 0:00 | \_ cat - 143374 pts/0 S+ 0:00 \_ target/debug/yash 143377 pts/0 S+ 0:00 \_ head
いろいろと無駄が多いが、いくつか便利な点もある。
パイプラインの各コマンドがシェルのメインのプロセスの子プロセスではなく孫プロセスやもっと遠い子孫プロセスとして実行されるといふことは、メインのプロセスはそれら子孫プロセスを wait することができないといふことであり、またその必要がないといふことでもある。パイプラインの各要素の実行が終はったらゾンビプロセスが残るが、それらのゾンビを回収するのはメインのプロセスの役目ではなくなるので、メインのプロセスがいちいち子プロセスの ID を管理する必要もなくなる。
パイプラインの各要素を直接の子プロセスにしない場合、他に以下の様な点に影響がある。
今の yash 2 では jobs -l
を実行したときにパイプラインの各要素のプロセス ID と状態を一行づつ表示する。Bash, dash, mksh, zsh も各要素のプロセス ID を表示する。これをするには、パイプラインの各要素をシェルの子プロセスとして実行しなければならない。しかしこの様な表示形式は POSIX では必須とされてをらず、ksh は一つのジョブに対して一つのプロセス ID しか表示しない。
Pipefail オプションを有効にすると、シェルはパイプラインに含まれる全ての要素の終了ステータスを考慮する必要がある。パイプラインの各要素をメインのプロセスの子プロセスとして実行する場合、どれかの要素がシグナルによって終了した場合はそれをメインのプロセスで認識することができるので、384 以上の終了ステータスを返すことができる。しかし、パイプラインの各要素が孫プロセスやもっと遠い子孫プロセスで実行される場合、メインのプロセスの子プロセスを通じてメインのプロセスに終了ステータスが伝はるとき、終了ステータスの最下位 8 ビットしか伝はらない。
Bash には PIPESTATUS
といふ特別な配列変数があって、これにはパイプラインの全ての要素の終了ステータスが格納される。Mksh と zsh にも同様なものがある。これを実装するには、パイプラインの各要素をメインのプロセスの子プロセスとして実行しなくてはいけない。
まとめると、POSIX で要求される範囲の動作を達成するだけなら、パイプラインの各要素はメインのプロセスの子プロセスである必要はなく、シェルは一つのジョブに対して一つだけプロセス ID を覚えておけばよい。Yash-rs では (少なくとも当面は) これを活かさうと思ふ。
| 固定リンク
コメント
一つのジョブに子プロセスが常に一つしかない場合、シェルが管理するジョブの状態を計算する上でも都合が良い。子プロセスが複数あってそれらの状態が異なるとき、ジョブ全体の状態はどうなるべきか? 子プロセスが一つだけなら悩むことがない。
投稿: まじかんと | 2022年4月17日 (日) 01時40分