服务器进程为何通常fork()两次
http://www.linuxidc.com/Linux/2011-08/40590.htm
前几天接到某互联网公司的电话面试,面试官问我两次fork()的作用,我一头雾水,说不知道。知识面还是太窄了。下面就总结下两次fork()的作用。
首先,要了解什么叫僵尸进程,什么叫孤儿进程,以及服务器进程运行所需要的一些条件。两次fork()就是为了解决这些相关的问题而出现的一种编程方法。
孤儿进程
孤儿进程是指父进程在子进程结束之前死亡(return 或exit)。如下图所示:
但是孤儿进程并不会像上面画的那样持续很长时间,当系统发现孤儿进程时,init进程就收养孤儿进程,成为它的父亲,child进程exit后的资源回收就都由init进程来完成。
僵尸进程
僵尸进程是指子进程在父进程之前结束了,但是父进程没有用wait或waitpid回收子进程。如下图所示:
父进程没有用wait回收子进程并不说明它不会回收子进程。子进程在结束的时候会给其父进程发送一个SIGCHILD信号,父进程默认是忽略SIGCHILD信号的,如果父进程通过signal()函数设置了SIGCHILD的信号处理函数,则在信号处理函数中可以回收子进程的资源。
事实上,即便是父进程没有设置SIGCHILD的信号处理函数,也没有关系,因为在父进程结束之前,子进程可以一直保持僵尸状态,当父进程结束后,init进程就会负责回收僵尸子进程。
但是,如果父进程是一个服务器进程,一直循环着不退出,那子进程就会一直保持着僵尸状态。虽然僵尸进程不会占用任何内存资源,但是过多的僵尸进程总还是会影响系统性能的。黔驴技穷的情况下,该怎么办呢?
这个时候就需要一个英雄来拯救整个世界,它就是两次fork()技法。
两次fork()技法
两次fork()的流程如下所示:
《UNIX环境高级编程》(下载见http://www.linuxidc.net/thread-2063-1-1.html)这本书里提供了两次fork的一个例子,代码如下:
- int main(void)
- {
- pid_t pid;
- if ( (pid = fork()) < 0)
- err_sys("fork error");
- else if (pid == 0)
- { /* first child */
- if ( (pid = fork()) < 0)
- err_sys("fork error");
- else if (pid > 0)
- exit(0); /* parent from second fork == first child */
- /* We're the second child; our parent becomes init as soon
- as our real parent calls exit() in the statement above.
- Here's where we'd continue executing, knowing that when
- we're done, init will reap our status. */
- sleep(2);
- printf("second child, parent pid = %d\n", getppid());
- exit(0);
- }
- if (waitpid(pid, NULL, 0) != pid) /* wait for first child */
- err_sys("waitpid error");
- /* We're the parent (the original process); we continue executing,
- knowing that we're not the parent of the second child. */
- exit(0);
- }
理所当然,第二个子进程的父进程是进程号为1的init进程。
一言以蔽之,两次fork()是为了防止系统中产生僵尸进程。
参考文献:《UNIX环境高级编程》下载见http://www.linuxidc.net/thread-2063-1-1.html 与 http://www.linuxidc.com/Linux/2011-04/34662.htm