コンピュータの世界では、親プロセスとは、1つ以上の子プロセスを作成したプロセスのことである。
Unix系オペレーティングシステムでは、プロセス0(スワッパー)を除くすべてのプロセスは、別のプロセスがfork()システムコールを実行したときに作成されます。forkを呼び出したプロセスは親プロセスであり、新しく作成されたプロセスは子プロセスです。すべてのプロセス(プロセス0を除く)は1つの親プロセスを持ちますが、複数の子プロセスを持つことができます。[ 1 ]
オペレーティングシステムのカーネルは、各プロセスをプロセス識別子で識別します。プロセス 0は、システム起動時に作成される特別なプロセスです。子プロセス(プロセス 1) をフォークした後、プロセス 0はスワッパー プロセス(「アイドル タスク」とも呼ばれる)になります。プロセス 1はinitと呼ばれ、システム内の他のすべてのプロセスの祖先です。[ 2 ]
Linuxカーネルでは、プロセスとPOSIXスレッドの差は非常に小さく、実親と親の2種類の親プロセスが存在します。親とは、子プロセスの終了時にSIGCHLDシグナルを受け取るプロセスであり、実親とは、マルチスレッド環境で実際にこの子プロセスを作成したスレッドです。通常のプロセスでは、これら2つの値は同じですが、プロセスとして動作するPOSIXスレッドでは、これら2つの値は異なる場合があります。[ 3 ]
オペレーティングシステムは、プロセス識別子(一般に「 pid 」と呼ばれる)によって各プロセスを、その動作に必要なデータと関連付けるテーブルを保持しています。プロセスの実行中、このようなデータには、プロセスに割り当てられたメモリセグメント、プロセスが呼び出された際の引数、環境変数、リソース使用量に関するカウンタ、ユーザーID、グループID、グループセット、そして場合によってはその他の種類の情報が含まれることがあります。
プロセスが、exit 関数を呼び出す(メイン関数からreturnコマンドを実行するなど、暗黙的に)か、突然終了させるシグナルを受信することによって実行を終了すると、オペレーティングシステムはそのプロセスに関連するリソースと情報のほとんどを解放しますが、リソースの使用状況と終了ステータスコードに関するデータは保持します。これは、親プロセスが、子プロセスが正常に実行されたかどうか(標準関数を使用して終了ステータス コードをデコードすることによって)、および実行中に消費されたシステム リソースの量を知る必要がある場合があるためです。
デフォルトでは、システムは親プロセスが子プロセスの終了時にそのような情報に関心を持っていると想定し、親プロセスにSIGCHLDシグナルを送信して、収集すべき子プロセスに関するデータがあることを通知します。このようなデータの収集は、waitファミリーの関数(wait自体、またはwaitpid、waitid、wait4などの関連関数)を呼び出すことによって行われます。この収集が完了するとすぐに、システムは子プロセスに関する最後の情報を解放し、プロセステーブルから子プロセスのPIDを削除します。しかし、親プロセスが子プロセスのデータの収集に時間をかけすぎたり(あるいは全く収集できなかったり)すると、システムは子プロセスのPIDと終了データをプロセステーブルに永久に保持せざるを得なくなります。
データが収集されずに終了したプロセスは、 UNIX用語ではゾンビプロセス、または単にゾンビと呼ばれます。この名前は、終了したプロセスは実際に機能を停止しているため「もはや生きていない」または「死んでいる」とみなされ、死んだままのプロセスが「生きている」プロセスの世界、つまりプロセス テーブルに「化身」しているため、実際には「不死身」または「ゾンビ」である、というユーモラスな比喩に基づいています。
ゾンビプロセスは、リソースが限られているシステムやプロセステーブルのサイズが限られているシステムでは問題となる可能性があります。なぜなら、長期間存続するゾンビプロセスがリソースを消費し続けることで、新しいアクティブなプロセスの作成が妨げられる可能性があるからです。
したがって、子プロセスを生成する可能性のあるプログラムでは、元の子プロセスから長期間ゾンビプロセスが形成されるのを防ぐコードを用意することが、優れたプログラミング手法です。最も明白なアプローチは、新しいプロセスを作成した後、どこかでwaitまたはそれに関連する関数を呼び出すコードを用意することです。プログラムが非同期で実行され、予測不可能な順序で終了する可能性のある多数の子プロセスを作成することが予想される場合は、一般的に、SIGCHLDシグナルのハンドラを作成し、ループ内でwaitファミリー関数のいずれかを呼び出し、未収集の子データがなくなるまで処理を続けるのが良いでしょう。親プロセスが子プロセスの終了を完全に無視してもゾンビプロセスを作成しないことは可能ですが、そのためには、特別なオプションフラグSA_NOCLDWAITを指定してsigactionを呼び出すことにより、 SIGCHLDのハンドラを明示的に定義する必要があります。[ 4 ]
孤児プロセスはゾンビプロセスとは正反対の状況であり、親プロセスが子プロセスより先に終了し、子プロセスが「孤児」になる場合を指します。子プロセスが終了すると(SIGCHLD シグナルを介して)非同期で子から親に通知されるのとは異なり、親プロセスが終了しても子プロセスにはすぐに通知されません。代わりに、システムは子プロセスのデータの「親 PID」フィールドを、システム内の他のすべてのプロセスの「祖先」となるプロセスに再定義します。このプロセスの PID は通常 1 であり、名前は伝統的に「init」です(Linux カーネル 3.4 以降を除く [詳細は後述])。そのため、init はシステム上のすべての孤児プロセスを「養子にする」と言われていました。[ 5 ] [ 6 ]
UNIX初心者プログラマーの間では、終了するプロセスの子プロセスは、そのプロセスの直近の親プロセス(つまり、それらの子プロセスの「祖父母」)に引き継がれるという思い込みがやや一般的だった。しかし、この思い込みは誤りだった。もちろん、その「祖父母」がinitプロセス自身である場合は別だが。
Linuxカーネル3.4以降では、これはもはや当てはまりません。実際には、プロセスはprctl()システムコールをPR_SET_CHILD_SUBREAPERオプション付きで発行することができ、その結果、プロセス#1ではなく、プロセス自身が、孤立した子孫プロセスの親になります。これは、 systemd、upstart、noshサービスマネージャなどの最新のサービスマネージャやデーモン監視ユーティリティの動作方法です。
これはマニュアルページの要約で、以下の内容を報告しています。
サブリーパーは、子孫プロセスに対して init(1) の役割を果たします。プロセスが孤立状態になった場合 (つまり、その直近の親プロセスが終了した場合)、そのプロセスは最も近い、まだ生きている祖先サブリーパーに再親付けされます。その後、孤立したプロセスで getppid() を呼び出すと、サブリーパー プロセスの PID が返されるようになり、孤立プロセスが終了すると、サブリーパー プロセスが SIGCHLD シグナルを受け取り、そのプロセスを待機 (2) して終了状態を検出できるようになります。[ 7 ]