1. 程式人生 > >Linux下的僵死程序以及其處理辦法

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、執行時發生

致命錯誤或收到終止訊號所導致)的時候核心釋放該程序所有的資源,包括開啟的檔案,佔用的記憶體等. 但是仍然為其保留一定的資訊(包括程序號the process ID,退出狀態the termination status of the process,執行時間the amount of CPU time taken by the process等), 直到父程序通過wait / waitpid來取時才釋放

但也正是這個機制導致瞭如果父程序並不呼叫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訊號的響應方式:忽略。

這種方式可以保持非同步,即處理僵死程序的同時,父程序還可以繼續執行不受影響。