Linux下的僵死程序以及其處理辦法
僵死程序概念:
1.父程序未結束,子程序結束。父程序未獲取子程序的退出資料。2.一個程序的主體釋放,pcb沒有釋放。
瞭解了僵死程序的概念之後我們就可以輕鬆的模擬出一個僵死程序:
父程序死迴圈,子程序執行一段時間後結束(或立刻結束。執行一段時間便於觀察)。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main () { pid_t p = fork(); if (p == -1) { printf("ERROR\n"); } else if (p == 0) { printf("Child pid is:%d\n",getpid()); exit(0); } else { while (1) { printf("Father pid is:%d\n",getpid()); sleep(1); } } }
這段程式碼就完成了建立僵死程序的任務。在執行之後,會列印輸出一次子程序的pid,隨後一直是父程序列印自己的pid。
這時在另一個控制檯中使用ps -e命令檢視程序,可以檢視到僵死的子程序。程序後有<defunct>字樣。
那麼,產生僵死程序的根本原因是什麼呢?
從概念我們可以知道是由於父程序和子程序是非同步執行的,即父程序永遠無法預測子程序何時結束,當然也就不知道何時去收集子程序的推出資訊了。
那麼,會不會出現父程序正忙(如例子中的while(1)),在子程序退出的時候沒有來得及獲取退出資訊從而導致丟失呢?
不會。因為在UNIX系統中提供了一種保護機制:每個程序在退出(一般是呼叫exit、執行時發生
但也正是這個機制導致瞭如果父程序並不呼叫wait,waitpid函式等,也不對訊號進行忽略的話,這些資訊就會一直儲存,程序號被一直佔用。如果僵死程序過多還會導致無法建立新程序的問題。
現在瞭解了僵死程序產生的原因還有危害之後,我們應該如何去處理它呢?
方法一:wait/waitpid函式:pid_t wait(int *status)
wait函式就是用來應對這種情況的,父程序在呼叫wait函式之後就可以將自己阻塞,由wait自動分析是否當前程序的某個子程序已經退出,如果讓它找到了這樣一個已經變成殭屍的子程序,wait就會收集這個子程序的資訊,並把它徹底銷燬後返回;如果沒有找到這樣一個子程序,wait就會一直阻塞在這裡,直到有一個出現為止。
其中的引數status用來儲存被收集程序退出時的一些狀態,它是一個指向int型別的指標。但如果我們對這個子程序是如何死掉的毫不在意,只想把這個殭屍程序消滅掉,(事實上絕大多數情況下,我們都會這樣想),我們就可以設定這個引數為NULL,就象下面這樣:
pid = wait(NULL);
在原始碼中的父程序塊中也只需要新增 wait(NULL),再執行的時候就會發現僵死程序已經被處理掉了。但是,這麼做的缺點也很明顯。在阻塞的過程中,父程序停止了自己的執行。在實際應用中我們不可能為了處理一個僵死程序而令父程序一直wait。並且一個wait函式只能處理一個僵死程序,作用十分有限。
方法二:將父程序中對SIGCHLD訊號的處理函式設為SIG_IGN(忽略)
這裡使用到了訊號,需要訊號的標頭檔案:signal.h.
同樣,只需要在父程序塊中加入一行程式碼:signal(SIGCHLD,SIG_IGN);,就可以不產生僵死程序了。呼叫這個signal函式就定義了父程序對子程序結束後返回的SIGCHLD訊號的響應方式:忽略。
這種方式可以保持非同步,即處理僵死程序的同時,父程序還可以繼續執行不受影響。