ゾンビプロセスを作成しない
Rev.4を表示中。最新版はこちら。
親プロセスがwaitで子の終了ステータスを取得しない間、子プロセスはゾンビ状態となります。しかし処理によっては、いつ終了するか分からない子プロセスのために、親プロセスは毎回wait状態で待つわけにいかない場合もあります。そこで子の終了をシグナルで親プロセスに知らせるという手法があります。struct sigaction saに子プロセスからの終了シグナルをハンドルすることで実現しています。親プロセスが子プロセスのそのシグナルを無視するか、ハンドラーにSIG_IGNを設定することで、子プロセスをゾンビ状態にしないことができ、子プロセスが呼び出したexit関数内からタスク構造体を解放することができるようです。
・補足
waitで待つ場合、親が呼び出したwait内から解放されます。
ゾンビ作成サンプル
#include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char** argv) { pid_t pid; /* ゾンビを作成しない struct sigaction sa; sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDWAIT; if (sigaction(SIGCHLD, &sa, NULL) == -1) { exit(1); } */ pid = fork(); if (pid == 0 ) { /* 子プロセス */ printf("This is child process.\n"); exit(0); } /* 親プロセス */ sleep(10); return 0; }実行結果
[root@localhost test]# ps PID TTY TIME CMD 2056 pts/0 00:00:02 bash 2400 pts/0 00:00:00 a.out 2401 pts/0 00:00:00 a.out <defunct> <- ゾンビ 2403 pts/0 00:00:00 ps先のプログラムのゾンビを作成しないのコメントを削除した結果
[root@localhost test]# ps PID TTY TIME CMD 2056 pts/0 00:00:02 bash 2410 pts/0 00:00:00 a.out 2413 pts/0 00:00:00 psこれを踏まえて実装を見てみます。do_exit関数からは、exit_notify関数で親プロセスへ知らせます。tracehook_notify_deathk関数は親プロセスにシグナルで待ち受けているかのチェックをしています。待ち受けているならdo_notify_parent関数が呼ばれ、その結果がDEATH_REAP(-1)なら子プロセスをEXIT_DEAD状態にし、タスク構造体を解放していました。
static void exit_notify(struct task_struct *tsk, int group_dead) { int signal; ・・・・ signal = tracehook_notify_death(tsk, &cookie, group_dead); if (signal >= 0) signal = do_notify_parent(tsk, signal); tsk->exit_state = signal == DEATH_REAP ? EXIT_DEAD : EXIT_ZOMBIE; ・・・・ if (signal == DEATH_REAP) release_task(tsk); }psigは親のsighand_structです。子プロセスがトレースされてなく親のプロセスがsa_handler == SIG_IGN または、sa_flags & SA_NOCLDWAITの時、ret=-1(DEATH_REAP)となります。なおsig=-1は、__group_send_sig_info関数で親プロセスに送信するための有効なシグナルかどうかの判断のようです。
int do_notify_parent(struct task_struct *tsk, int sig) { struct sighand_struct *psig; ・・・ psig = tsk->parent->sighand; spin_lock_irqsave(&psig->siglock, flags); if (!tsk->ptrace && sig == SIGCHLD && (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN || (psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) { ret = tsk->exit_signal = -1; if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) sig = -1; } if (valid_signal(sig) && sig > 0) __group_send_sig_info(sig, &info, tsk->parent); ・・・ return ret; }