ゾンビプロセスと孤児プロセス
さて、前回までで fork とかファイルとかのことはだいたいわかってきたかと思います。今回は、「親が死んだ子供は養子になるしかない」「子供が親の見てないところで死ぬとゾンビになってしまう」という話をします。
親が死んだ子供は養子になるしかない
今回はここ数日なにかと話題の Perl で行きましょう。
use strict;
use warnings;
my $pid = fork;
if ( $pid ) {
# 親プロセスで waitpid しないで
# 死んじゃう
sleep 1;
exit;
}
else {
#子プロセス
# getppid で親プロセスのpidを取得する
print getppid."\n";
#親が死ぬまで待つ
sleep 2;
# 親が死んだあとの親プロセスのpidって??
print getppid."\n";
}
さて、上のような perl スクリプトを実行すると、結果はどうなるでしょうか。子プロセスのほうの一回目の getppid では、まだ親プロセスが生きているので、当然そのプロセスの pid が表示されます。1秒後、親プロセスが終わるのでプロンプトが戻ってきます。そして、そのさらに1秒後、二度目の getppid が実行され、すでにプロンプトが戻っているターミナルに「1」と表示されるはずです。
これはどういうことかと言うと、親が死んだから、init さんのところに養子に入ったわけですね。親プロセスに先立たれて親の pid が存在しなくなったプロセスは、init が代わりに親プロセスとして振る舞ってくれます。
「子供が親の見てないところで死ぬとゾンビになってしまう」
「ゾンビプロセス」ってのを聞いたことあると思うんですけど、これはどういうプロセスなんでしょうか。コードで見てみましょう
use strict;
use warnings;
my $pid = fork;
if ( $pid ) {
print "$pid\n";
# 無限ループに忙しくて子プロセスを wait してない
while ( 1 ) {
sleep;
}
}
else {
# 子プロセスは即死する
exit;
}
上記のようなスクリプトを zombie.pl として保存して、バックグラウンド実行してみましょう
$ perl zombie.pl &
親プロセスの print "$pid\n" が利いて、子プロセスのpidが出力されたかと思います。さて、この親プロセスは、まだバックグラウンドで無限ループしています。一方、子プロセスは即 exit しているので、もう実行が終了しています。しかし、親はこの終了を wait していません。この子プロセスは、実行がおわってもう死んでいるのに、誰にも看取られていない(wait されていない)状態です。
そこで、先ほどターミナルに表示された pid がどうなっているのか、ps コマンドで確認してみましょう。
$ ps <さっきターミナルに表示されたpid>
どうなりましたか?環境によって多少の違いはあるかもしれませんが、私の環境では
PID TTY STAT TIME COMMAND
3668 pts/2 Z 0:00 [perl] <defunct>
と表示されました。STAT の部分に Z と出ていますね。これは、このプロセスがゾンビプロセスとなっていることを表します。
それでは、無限ループ中の親プロセスを fg でフォアグラウンドに戻して、Ctrl + Cで止めましょう。その状態で再度 ps でプロセスの状態を見てみると、さっきまでゾンビだったプロセスも、無事に成仏してなくなっていることが確認できると思います。
なにが起こったのか、説明しましょう。
さきほど、Ctrl+C で親プロセスを終了しましたが、この親は子を wait しないで死んでしまいました。ゾンビ状態だった子プロセスは、親プロセスが死んでしまったことにより、init さんの養子に入ります。すると、init さんが即座に wait でこのプロセスを看取ってくれて、無事にゾンビ状態だったプロセスが終了できたわけです。
まとめ
さて、このことから、以下のようなことがわかります。
- 子プロセスが実行終了しているにもかかわらず、親プロセスに wait されないとプロセスが回収されず、ゾンビプロセスとして残ってしまう
- 親プロセスが子プロセスを wait せずに先に死んでしまったときには、initプロセスが代わりに親となって子プロセスを wait してくれる
短いですが、今日はこんなところで。
次回予告
次回はようやくシグナルについて書く予定です。共有メモリの話と、スレッドの話もその後にできればしたいけど、気力ないかもしれないので次回が最終回の可能性が微レ存……