In UNIX System terminology, a process that has terminated,but whose parent has not yet waited for it, is called a zombie. 在UNIX 系統中,一個進程結束了,但是他的父進程沒有等待(調用wait / waitpid)他, 那么他將變成一個殭屍進程。 但是如果該進程的父進程已經先結束了,那么該進程就不會變成殭屍進程, 因為每個進程結束的時候,系統都會掃描當前系統中所運行的所有進程, 看有沒有哪個進程是剛剛結束的這個進程的子進程,如果是的話,就由Init 來接管他,成為他的父進程……
由於子進程的結束和父進程的運行是一個異步過程,即父進程永遠無法預測子進程 到底什麼時候結束. 那么會不會因為父進程太忙來不及wait子進程,或者說不知道 子進程什麼時候結束,而丟失子進程結束時的狀態信息呢? 不會。因為UNⅨ提供了一種機制可以保證只要父進程想知道子進程結束時的狀態信息, 就可以得到。這種機制就是: 在每個進程退出的時候,核心釋放該進程所有的資源,包括打開的檔案,占用的記憶體等。但是仍然為其保留一定的信息(包括進程號the process ID,退出狀態the termination status of the process,運行時間the amount of CPU time taken by the process等)。直到父進程通過wait / waitpid來取時才釋放. 但這樣就導致了問題,如果進程不調用wait / waitpid的話,那么保留的那段信息就不會釋放,其進程號就會一直被占用,但是系統所能使用的進程號是有限的,如果大量的產生殭屍進程,將因為沒有可用的進程號而導致系統不能產生新的進程. 此即為殭屍進程的危害,應當避免。
Example Recall our discussion in Section 8.5 about zombie processes. If we want to write a process so that it forks a child but we don't want to wait for the child to complete and we don't want the child to become a zombie until we terminate,the trick is to call fork twice. The program in Figure 8.8 does this. We call sleep in the second child to ensure that the first child terminates before printing the parent process ID. After a fork,either the parent or the child can continue executing; we never know which will resume execution first. If we didn't put the second child to sleep,and if it resumed execution after the fork before its parent,the parent process ID that it printed would be that of its parent,not process ID 1. Executing the program in Figure 8.8 gives us $ ./a.out $ second child,parent pid = 1 Note that the shell prints its prompt when the original process terminates,which is before the second child prints its parent process ID. Figure 8.8. Avoid zombie processes by calling fork twice
#include "apue.h"
#include <sys/wait.h>
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 soonas our real parent calls exit() in the statement above. Here's where we'd continue executing,knowing that whenwe're done,init will reap our status.*/