Linux 僵死程序及其處理方法

2021-08-28 12:20:38 字數 3743 閱讀 1990

首先核心會釋放終止程序(呼叫了exit系統呼叫)所使用的所有儲存區,關閉所有開啟的檔案等,但核心為每乙個終止子程序儲存了一定量的資訊。這些資訊至少包括程序id,程序的終止狀態,以及該程序使用的cpu時間,所以當終止子程序的父程序呼叫wait或waitpid時就可以得到這些資訊。

而殭屍程序就是指:乙個程序執行了exit系統呼叫退出,而其父程序並沒有為它收屍(呼叫wait或waitpid來獲得它的結束狀態)的程序。

任何乙個子程序(init除外)在exit後並非馬上就消失,而是留下乙個稱外殭屍程序的資料結構,等待父程序處理。這是每個子程序都必需經歷的階段。另外子程序退出的時候會向其父程序傳送乙個sigchld訊號。

設定殭屍狀態的目的是維護子程序的資訊,以便父程序在以後某個時候獲取。這些資訊至少包括程序id,程序的終止狀態,以及該程序使用的cpu時間,所以當終止子程序的父程序呼叫wait或waitpid時就可以得到這些資訊。如果乙個程序終止,而該程序有子程序處於殭屍狀態,那麼它的所有殭屍子程序的父程序id將被重置為1(init程序)。繼承這些子程序的init程序將清理它們(也就是說init程序將wait它們,從而去除它們的殭屍狀態)。

殭屍程序的pid還佔據著,意味著海量的子程序會佔據滿程序表項,會使後來的程序無法fork.

殭屍程序的核心棧無法被釋放掉,為啥會留著它的核心棧,因為在棧的最低端,有著thread_info結構,它包含著 struct_task 結構,這裡面包含著一些退出資訊

回答這個問題很簡單, 父程序和子程序終止關係有兩種:父程序先於子程序終止和子程序先於父程序終止。

孤兒程序——父程序先於子程序終止

終止程序的子程序的父程序更改為init程序,也就是父程序id更改為1。當乙個程序終止時,核心會檢查所有的活動程序,找出正要終止程序的子程序並將其父程序更改為init程序。

殭屍程序———子程序先於父程序終止

核心會為每個終止子程序保留一定量的資訊,父程序就可以通過呼叫wait函式來獲取這些資訊。如果父程序沒有呼叫wait函式的話,則該資源就會一直被占用。

1.wait和waitpid函式:父程序呼叫wait/waitpid等函式等待子程序結束,如果尚無子程序退出wait會導致父程序阻塞。waitpid可以通過傳遞wnohang使父程序不阻塞立即返回。 

2.sigaction訊號處理函式(交給核心處理):如果父程序很忙可以用sigaction註冊訊號處理函式,在訊號處理函式呼叫wait/waitpid等待子程序退出。(sigaction函式類似於signal函式,而且完全可以代替後者,也更穩定)

3.signal忽略sigchld訊號(交給核心處理) :通過signal(sigchld, sig_ign)通知核心對子程序的結束不關心,由核心**。如果不想讓父程序掛起,可以在父程序中加入一條語句:signal(sigchld,sig_ign);表示父程序忽略sigchld訊號,該訊號是子程序退出的時候向父程序傳送的。

4.fork兩次:通過兩次呼叫fork。父程序首先呼叫fork建立乙個子程序然後waitpid等待子程序退出,子程序再fork乙個孫程序後退出。這樣子程序退出後會被父程序等待**,而對於孫子程序其父程序已經退出所以孫程序成為乙個孤兒程序,孤兒程序由init程序接管,孫程序結束後,init會等待**。

第三種方法忽略sigchld訊號,這常用於併發伺服器的效能的乙個技巧因為併發伺服器常常fork很多子程序,子程序終結之後需要伺服器程序去wait清理資源。如果將此訊號的處理方式設為忽略,可讓核心把殭屍子程序轉交給init程序去處理,省去了大量殭屍程序占用系統資源

#include #include pid_t wait(int *status);
程序一旦呼叫了wait,就立即阻塞自己,由wait自動分析是否當前程序的某個子程序已經退出,如果讓它找到了這樣乙個已經變成殭屍的子程序,wait就會收集這個子程序的資訊,並把它徹底銷毀後返回;如果沒有找到這樣乙個子程序,wait就會一直阻塞在這裡,直到有乙個出現為止。 

引數status用來儲存被收集程序退出時的一些狀態,它是乙個指向int型別的指標。但如果我們對這個子程序是如何死掉的毫不在意,只想把這個殭屍程序消滅掉,(事實上絕大多數情況下,我們都會這樣想),我們就可以設定這個引數為null,就象下面這樣:

pid = wait(null);
如果成功,wait會返回被收集的子程序的程序id,如果呼叫程序沒有子程序,呼叫就會失敗,此時wait返回-1,同時errno被置為echild。

可以上述的一些巨集判斷子程序的退出情況:

#include #include pid_t waitpid(pid_t pid, int *status, int options);
引數:

status:如果不是空,會把狀態資訊寫到它指向的位置,與wait一樣

options:允許改變waitpid的行為,最有用的乙個選項是wnohang,它的作用是防止waitpid把呼叫者的執行掛起。

返回值:如果成功返回等待子程序的id,失敗返回-1

對於waitpid的p i d引數的解釋與其值有關:

pid==-1 等待任一子程序。此種情況下,waitpid等效於wait。 pid>0 等待程序id為pid的子程序。 pid==0 等待呼叫者程序組內的任一子程序。 pid

引數options是以下各標誌的按位或運算或為0。

常量 說明 wcontinued 等待一進城,它以前曾被停止,此後又繼續,但狀態尚未報告。 wexited 等待已退出的程序。 wnohang 如無可用的子程序退出狀態,立即返回而非阻塞。 wnowait 不破壞子程序退出狀態。該子程序退出狀態可由後續的wait、waitid或waitpid呼叫獲取。 wstopped 等待一程序,它已經停止,但其狀態尚未報告。

示例:如以下**會建立100個子程序,但是父程序並未等待它們結束,所以在父程序退出前會有100個殭屍程序。

#include #include int main() 

if(pid>0)

return 0;

}

其中乙個解決方法即是編寫乙個sigchld訊號處理程式來呼叫wait/waitpid來等待子程序返回。

#include #include #include #include #include void wait4children(int signo) 

int main()

if(pid>0)

return 0;

}

但是通過執行程式發現還是會有殭屍程序,而且每次殭屍程序的數量都不定。這是為什麼呢?其實主要是因為linux的訊號機制是不排隊的,假如在某一時間段多個子程序退出後都會發出sigchld訊號,但父程序來不及乙個乙個地響應,所以最後父程序實際上只執行了一次訊號處理函式。但執行一次訊號處理函式只等待乙個子程序退出,所以最後會有一些子程序依然是殭屍程序。

雖然這樣但是有一點是明了的,就是收到sigchld必然有子程序退出,而我們可以在訊號處理函式裡迴圈呼叫waitpid函式來等待所有的退出的子程序。至於為什麼不用wait,主要原因是在wait在清理完所有殭屍程序後再次等待會阻塞。

#include #include #include #include #include void read_childproc(int sig)

}int main()

else

else

}}return 0;

}

部分參考jessica的

部分參考尹聖雨《tcp/tp 網路程式設計》

linux 僵死程序的處理

1.僵死程序 程序呼叫exit後,儲存區已經釋放,描述符已經關閉後,核心還為每乙個程序保留了一定資訊 程序id 終止狀態 使用的cpu時間等 需要其父程序呼叫wait waitpid。若父程序沒有對其呼叫wait。則子程序就是僵死程序。如果父程序先於子程序結束,則該子程序父程序變成了init 1號程...

linux僵死程序

乙個程序在呼叫exit命令結束自己的生命的時候,其實它並沒有真正的被銷毀,而是留下乙個稱為僵死程序 zombie 的資料結構 系統呼叫exit,它的作用是使程序退出,但也僅僅限於將乙個正常的程序變成乙個僵死程序,並不能將其完全銷毀 一 僵死程序的產生 在每個程序退出的時候,核心釋放該程序所有的資源,...

linux僵死程序

乙個程序在呼叫exit命令結束自己的生命的時候,其實它並沒有真正的被銷毀,而是留下乙個稱為僵死程序 zombie 的資料結構 系統呼叫exit,它的作用是使程序退出,但也僅僅限於將乙個正常的程序變成乙個僵死程序,並不能將其完全銷毀 一 僵死程序的產生 在每個程序退出的時候,核心釋放該程序所有的資源,...