exit(2)とpid namespaceめも

プロセスが終了するときにもpid namespaceが関係するので、その辺りのめもです。

流れとしてはこんな感じです。

do_exit()@kernel/exit.c
  -> exit_notify@kernel/exit.c
    -> forget_original_parent()@kernel/exit.c
      -> find_child_reaper()@kernel/exit.c
        -> zap_pid_ns_processes()@kernel/pid_namespace.c

今回読むのはzap_pid_ns_processes()です。 プロトタイプはこうです。

184 void zap_pid_ns_processes(struct pid_namespace *pid_ns)

この関数はPID Namespace(pid_ns)に属するプロセスに対してSIGKILLを投げていって、プロセスを終了させるのが仕事です。 最初にこの名前空間にこれ以上プロセスが作れないようにPID Namespaceをロックします。次はカレントプロセスに対してSIGCHLDを無視するようにします。そして、名前空間内の各プロセスに対してSIGKILLを投げていきます。このときはpid_task()を使ってpidからpidに該当するtask_struct構造体を取得します。

216         read_lock(&tasklist_lock);
217         nr = next_pidmap(pid_ns, 1);
218         while (nr > 0) {
219                 rcu_read_lock();
220 
221                 task = pid_task(find_vpid(nr), PIDTYPE_PID);
222                 if (task && !__fatal_signal_pending(task))
223                         send_sig_info(SIGKILL, SEND_SIG_FORCED, task);
224 
225                 rcu_read_unlock();
226 
227                 nr = next_pidmap(pid_ns, nr);
228         }
229         read_unlock(&tasklist_lock);

KILLしまくったら次はSIGCHLDを無視している間にゾンビ状態(EXIT_ZOMBIE)になってしまった子プロセスを回収します。使うのはsys_wait4()です。ただし、sys_wait4()だとEXIT_DEADになったプロセスは回収できないとのことですが、コメントによるとEXIT_DEADのプロセスは(゚ε゚)キニシナイ!!とのことです。これはinit_pid_nsのinitプロセスに任せるようです。まあ、名前空間はツリー構造なのでこの名前空間にいるプロセスはツリー構造の上にある名前空間から参照できますしね。 つぎにカレントプロセスをTASK_UNINTERRUPTIBLE状態にして、pid_ns->nr_hashed == init_pidsになるまでshcedule()を呼んで待ちます。

init_pidsは関数の最初のほうで、カレントプロセスがスレッドグループリーダーなら1、そうじゃなければ2と設定しています。

189         int init_pids = thread_group_leader(me) ? 1 : 2;

待ち状態を抜けたらカレントプロセスの状態をTASK_RUNNINGに戻します。そして、pid_ns構造体のreboot変数が0でない場合はcurrent->signal->group_exit_codeにその値を設定します。関数の最後にacct_exit_ns()を呼び、アカウンティング情報の後始末をします。