init进程会托管zombie进程,并且在它退出的时候,init进程会去回收zombie进程中的内存结构。这个回收工作肯定是调用wait函数。很好奇,这段wait代码到底在哪,翻了一下代码,终于找到了这段init进程调用wait函数,来结束子进程的代码。
进程在结束的时候,如果父进程先于子进程结束了,那么父进程结束的时候会把重新设置它的子进程的父子关系,就是把它的所有子进程的父进程改为当前pid namespace中的child_reaper,那么这些子进程结束的时候向child_reaper进程发送信号SIGCHLD,那么在初始pid namespace中child_reaper就是init进程,init进程是进程号为1的进程,它是在内核中创造的第一个进程。
代码:init/main.c linux-2.6.35.13
-
-
429 static noinline void __init_refok rest_init(void)
-
430 __releases(kernel_lock)
-
431 {
-
432 int pid;
-
433
-
434 rcu_scheduler_starting();
-
435 /*
-
436 * We need to spawn init first so that it obtains pid 1, however
-
437 * the init task will end up wanting to create kthreads, which, if
-
438 * we schedule it before we create kthreadd, will OOPS.
-
439 */
-
440 kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
可以看到start_kernel函数调用了rest_init函数,这个函数中会启动第一个进程,执行函数kernel_init,这个进程就是init进程,下面是init进程的启动过程。
-
-
866 static int __init kernel_init(void * unused)
-
867 {
-
868 /*
-
869 * Wait until kthreadd is all set-up.
-
870 */
-
871 wait_for_completion(&kthreadd_done);
-
872 lock_kernel();
-
873
-
874 /*
-
875 * init can allocate pages on any node
-
876 */
-
877 set_mems_allowed(node_states[N_HIGH_MEMORY]);
-
878 /*
-
879 * init can run on any cpu.
-
880 */
-
881 set_cpus_allowed_ptr(current, cpu_all_mask);
-
882 /*
-
883 * Tell the world that we're going to be the grim
-
884 * reaper of innocent orphaned children.
-
885 *
-
886 * We don't want people to have to make incorrect
-
887 * assumptions about where in the task array this
-
888 * can be found.
-
889 */
-
890 init_pid_ns.child_reaper = current; //init_pid_ns就是最初的pid命名空间,把这个空间的child_reaper设置为当前进程,就是init进程。
init进程中会去执行/sbin/init程序
-
-
857 run_init_process("/sbin/init");
也就是说init进程在这里后面的工作就是在/sbin/init程序中了,这个init是用户态的程序,init进程的有很多重要的任务,但是在这里我只关心它是如何处理处理zombie进程的,它有多种实现方式,看了一种基于事件的实现,,同时它依赖于库,找到了upstart的main函数,最后的调用了nih的轮询事件的函数。
代码:Upstart-1.6.1 init/main.c
-
-
606 /* Run through the loop at least once to deal with signals that were
-
607 * delivered to the previous process while the mask was set or to
-
608 * process the startup event we emitted.
-
609 */
-
610 nih_main_loop_interrupt ();
-
611 ret = nih_main_loop (); //进入nih库中的时间循环函数
在nih中,找到 nih/main.c
-
-
543 nih_signal_set_handler (SIGCHLD, nih_signal_handler); //设置sig handler
-
544
-
545 while (! exit_loop) {
-
546 NihTimer *next_timer;
-
547 struct timespec now;
-
548 struct timeval timeout;
-
549 fd_set readfds, writefds, exceptfds;
可以看到,这个nih_main_loop就是一个循环事件的函数,nih_signal_handler 是信号SIGCHLD的处理函数,这个函数通过管道interrupt_pipe 和nih_main_loop函数联系起来,这个函数就是向管道中写数据。
-
-
624 void
-
625 nih_main_loop_interrupt (void)
-
626 {
-
627 nih_main_loop_init ();
-
628
-
629 if (interrupt_pipe[1] != -1)
-
630 while (write (interrupt_pipe[1], "", 1) < 0) //向管道写入数据
-
631 ;
-
632 }
主循环在循环过程中会阻塞在read函数上,此时管道的一端写入了数据,同时管道的令一端也就是main函数中会在read函数中唤醒,执行后面的nih_child_poll函数,来处理子进程的结束退出。
-
-
595 while (read (interrupt_pipe[0], buf, sizeof (buf)) > 0) //阻塞读
-
596 ;
-
597 nih_signal_poll ();
-
598
-
599 /* Deal with terminated children */
-
600 nih_child_poll (); //处理子进程退出,里面时wait函数
nih_child_poll 函数就是处理任意进程结束的,可以想到这里肯定是调用wait函数了。
代码:ntl/child.c
-
-
141 void
-
142 nih_child_poll (void)
-
143 {
-
144 siginfo_t info;
-
145
-
146 nih_child_init ();
-
147
-
148 /* NOTE: there's a strange kernel inconsistency, when the waitid()
-
149 * syscall is native, it takes special care to zero this struct
-
150 * before returning ... but when it's a compat syscall, it
-
151 * specifically *doesn't* zero the struct.
-
152 *
-
153 * So we have to take care to do it ourselves before every call.
-
154 */
-
155 memset (&info, 0, sizeof (info));
-
156
-
157 while (waitid (P_ALL, 0, &info, WAITOPTS | WNOHANG) == 0) {
-
158 pid_t pid;
-
159 NihChildEvents event;
从代码中看到了会循环调用waitid函数,返回值0表示wait函数的回收工作成功(回收工作就是删除进程中其他的内存数据结构,zombie进程在结束时会保留一些数据结构),子进程结束了,同时可能是有多个子进程结束了,这时需要循环调用waitid,以便让所有结束的子进程完成回收工作。
总结:
父进程是init进程的进程在退出的时候,init进程会调用wait函数来处理所有子进程的退出,来清理进程剩下的内存结构。在不同的系统中,可能init进程的实现不相同,但是肯定子进程结束发出信号SIGCHLD,然后init进程调用wait函数来回收进程其他的内存结构。
参考文献:
1.http://blog.chinaunix.net/uid-23769728-id-3127671.html
2.
阅读(1823) | 评论(0) | 转发(0) |