私のメインプロセスは子プロセスを生成します。メインプロセスが強制終了された場合、子にはppid 1が割り当てられます。子が終了すると、initがこの子に対してwait()を呼び出さなかったため、ゾンビになります。この状況を回避する方法はありますか?
2 に答える
init
wait()
継承するプロセスを呼び出します。ゾンビは、子が終了したが、親はまだ存在しているが、まだ終了コードを取得していない場所にのみ存在する必要があります。マンページからinit
:
init
システム上のすべてのプロセスの親であり、カーネルによって実行され、他のすべてのプロセスを開始する責任があります。それは、自然の親が死んだすべてのプロセスの親であり、死んだときにそれらを刈り取る責任があります。
孤児(彼らはまだ生きていて、彼らの親は死んでいるので、彼らは養子縁組されていinit
ます)とゾンビ(彼らは死んでいますが、彼らの親は生きていて、まだ彼らを刈り取っていません)を区別する必要があります。
孤児は、退出した後、刈り取る前の非常に短い期間ゾンビになりinit
ますが、この期間は誰も気付かないほど十分に小さいはずです。実際、すべての既存のプロセス(おそらくinit
それ自体を除く)はこの短いゾンビフェーズを通過します。親が気付くほど迅速に刈り取らない場合のみです。
init
乳幼児死亡( )ハンドラーの実際のコードSIGCHLD
は次のようになります(私のコメント):
void chld_handler (int sig) {
CHILD *ch;
int pid, st;
int saved_errno = errno;
while ((pid = waitpid(-1, &st, WNOHANG)) != 0) { // << WAIT done here
if (errno == ECHILD) break;
for (ch = family; ch; ch = ch->next) {
if (ch->pid == pid && (ch->flags & RUNNING)) {
INITDBG (L_VB, child_handler: marked %d as zombie", ch->pid);
ADDSET (got_signals, SIGCHLD);
ch->exstat = st;
ch->flags |= ZOMBIE; // Set to zombie here.
if (ch->new) {
ch->new->exstat = st;
ch->new->flags |= ZOMBIE;
}
break;
}
}
if (ch == NULL) {
INITDBG (L_VB, "chld_handler: unknown child %d exited.", pid);
}
}
errno = saved_errno;
}
そして、後でメインループ(シグナルハンドラではない)で、としてマークされたプロセステーブル内のすべてのプロセスZOMBIE
がクリーンアップされます。
if (ISMEMBER (got_signals, SIGCHLD)) {
INITDBG(L_VB, "got SIGCHLD");
/* First set flag to 0 */
DELSET(got_signals, SIGCHLD);
/* See which child this was */
for (ch = family; ch; ch = ch->next) {
if (ch->flags & ZOMBIE) { // ZOMBIE detected here
INITDBG (L_VB, "Child died, PID= %d", ch->pid);
ch->flags &= ~(RUNNING|ZOMBIE|WAITING); // And cleared here.
if (ch->process[0] != '+') {
write_utmp_wtmp ("", ch->id, ch->pid, DEAD_PROCESS, NULL);
}
}
}
}
プログラムの最後に1つのwait(NULL)ステートメントを配置して、親と子が両方とも実行を完了し、どちらからもゾンビプロセスにならないように、お互いを待機します。