ゾンビプロセスと孤児プロセス

さて、前回までで 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 でこのプロセスを看取ってくれて、無事にゾンビ状態だったプロセスが終了できたわけです。

まとめ

さて、このことから、以下のようなことがわかります。

短いですが、今日はこんなところで。

次回予告

次回はようやくシグナルについて書く予定です。共有メモリの話と、スレッドの話もその後にできればしたいけど、気力ないかもしれないので次回が最終回の可能性が微レ存……