http://blog.chinaunix.net/uid-23089249-id-210808.html
一個程式在呼叫exit命令結束自己的生命的時候,其實它並沒有真正的被銷燬, 而是留下一個稱為僵死程式(Zombie)的資料結構(系統呼叫exit,它的作用是使程式退出,但也僅僅限於將一個正常的程式變成一個僵死程式,並不能將其完全銷燬)。
一、僵死程式的產生
在每個程式退出的時候,核心釋放該程式所有的資源,包括開啟的檔案,佔用的記憶體等,但是仍然為其保留一定的資訊(包括程式號the process ID,退出狀態the
termination status of the process,執行時間the amount of CPU time taken by the process等), 直到父程式通過wait/waitpid來取時才釋放。此時該程式處於僵死狀態,該程式成為僵死程式(Zombie Process)。 這保證了父程式可以獲取到子程式結束時的狀態資訊。
在Linux程式的狀態中,僵死程式是非常特殊的一種,它已經放棄了幾乎所有記憶體空間,沒有任何可執行程式碼,也不能被排程,僅僅在程式列表中保留一個位置,記載該程式的退出狀態等資訊供其他程式收集,除此之外,僵死程式不再佔有任何記憶體空間。它需要它的父程式來為它收屍,如果他的父程式沒安裝SIGCHLD訊號處理函式呼叫wait或waitpid()等待子程式結束,又沒有顯式忽略該訊號,那麼它就一直保持僵死狀態,如果這時父程式結束了,僵死的子程式成為"孤兒程式",過繼給1號程式init,init始終會負責清理僵死程式,它產生的所有僵死程式也跟著消失(每個程式結束的時候,系統都會掃描當前系統中所執行的所有程式, 看有沒有哪個程式是剛剛結束的這個程式的子程式,如果是的話,就由Init來接管他,成為他的父程式)。但是如果如果父程式是一個迴圈,不會結束,那麼子程式就會一直保持僵死狀態,這就是為什麼系統中有時會有很多的僵死程式。怎麼檢視僵死程式,利用命令ps,可以看到有標記為Z的程式就是僵死程式。
查詢所有的僵死程式
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
二、僵死程式的危害
如果父程式不呼叫wait/waitpid的話, 那麼保留的那段資訊就不會釋放,其程式號會一定被佔用,但是系統所能使用的程式號是有限的,如果產生了大量的僵死程式,將因為沒有可用的程式號而導致系統不能產生新的程式。
三、僵死程式的避免
1、父程式通過wait和waitpid等函式等待子程式結束,這會導致父程式掛起
2、如果父程式很忙,那麼可以用signal函式為SIGCHLD安裝訊號處理函式。子程式結束後,父程式會收到該訊號,可以在訊號處理函式中呼叫wait回收 。
3、如果父程式不關心子程式什麼時候結束,那麼可以用signal(SIGCHLD, SIG_IGN)通知核心,自己對子程式的結束不感興趣,那麼子程式結束後,核心會回收, 並不再給父程式
傳送訊號。
或用sigaction函式為SIGCHLD設定SA_NOCLDWAIT,這樣子程式結束後,就不會進入僵死狀態
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sa.sa_flags = SA_NOCLDWAIT;
sigemptyset(&sa.sa_mask);
sigaction(SIGCHLD, &sa, NULL);
4、fork兩次,父程式fork一個子程式,然後繼續工作,子程式fork一個孫程式後退出,那麼孫程式被init接管,孫程式結束後,init會回收。不過子程式的回收還要父程式來做。
- int nStatus;
- pid_t pid;
-
- pid = vfork(); //生成子程式
- if (pid > 0) //父程式
- {
- waitpid(pid, &nStatus, 0); //等待子程式結束,否則子程式會成為僵死程式,一直存在,即便子程式已結束執行
- }
- else if (0 == pid) //子程式
- {
- pid = vfork(); //生成孫程式
- if (pid > 0)
- {
- exit(0); //子程式退出,孫程式過繼給init程式,其退出狀態也由init程式處理,與原有父程式無關
- }
- else if (0 == pid) //孫程式
- {
- if (execlp("ls", "ls", NULL) < 0)
- {
- perror("execlp");
- exit(-1);
- }
- }
- else
- {
- perror("vfork(child)");
- }
- }
- else
- {
- perror("vfork(parent)");
- }
- }