當我們只fork()一次後,存在父進程和子進程。這時有兩種方法來避免產生僵屍進程:
父進程調用waitpid()等函數來接收子進程退出狀態。
父進程先結束,子進程則自動托管到Init進程(pid = 1)。
目前先考慮子進程先於父進程結束的情況:
若父進程未處理子進程退出狀態,在父進程退出前,子進程一直處於僵屍進程狀態。
若父進程調用waitpid()(這裡使用阻塞調用確保子進程先於父進程結束)來等待子進程結束,將會使父進程在調用waitpid()後進入睡眠狀態,只有子進程結束父進程的waitpid()才會返回。 如果存在子進程結束,但父進程還未執行到waitpid()的情況,那麼這段時期子進程也將處於僵屍進程狀態。
由此,可以看出父進程與子進程有父子關系,除非保證父進程先於子進程結束或者保證父進程在子進程結束前執行waitpid(),子進程均有機會成為僵屍進程。那麼如何使父進程更方便地創建不會成為僵屍進程的子進程呢?這就要用兩次fork()了。
父進程一次fork()後產生一個子進程隨後立即執行waitpid(子進程pid, NULL, 0)來等待子進程結束,然後子進程fork()後產生孫子進程隨後立即exit(0)。這樣子進程順利終止(父進程僅僅給子進程收屍,並不需要子進程的返回值),然後父進程繼續執行。這時的孫子進程由於失去了它的父進程(即是父進程的子進程),將被轉交給Init進程托管。於是父進程與孫子進程無繼承關系了,它們的父進程均為Init,Init進程在其子進程結束時會自動收屍,這樣也就不會產生僵屍進程了。
#include <stdio.h> #include <sys/wait.h> #include <sys/types.h> #include <unistd.h> int main(void) { pid_t pid; if ((pid = fork()) < 0) { fprintf(stderr,"Fork error!/n"); exit(-1); } else if (pid == 0) /* first child */ { if ((pid = fork()) < 0) { fprintf(stderr,"Fork error!/n"); exit(-1); } 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 */ { fprintf(stderr,"Waitpid error!/n"); exit(-1); } /* * We're the parent (the original process); we continue executing, * knowing that we're not the parent of the second child. */ exit(0); }