Defunct程序處理方式

2021-06-28 09:43:14 字數 3832 閱讀 1264

本文**:

下面談談 defunct 程序,中文翻譯叫殭屍程序。下文整理於網路以及apue一書。

一、什麼是殭屍程序

在unix 系統中,乙個程序結束了,但是他的父程序沒有等待(呼叫wait / waitpid)他,

那麼他將變成乙個殭屍程序。

當用ps命令觀察程序的執行狀態時,看到這些程序的狀態列為defunct。殭屍程序是乙個早已死亡的程序,但在程序表(processs table)中仍佔了乙個位置(slot)。

但是如果該程序的父程序已經先結束了,那麼該程序就不會變成殭屍程序。

因為每個程序結束的時候,系統都會掃瞄當前系統中所執行的所有程序,看

看有沒有哪個程序是剛剛結束的這個程序的子程序,如果是的話,就由init程序

來接管他,成為他的父程序,從而保證每個程序都會有乙個父程序。

而init程序會自動wait其子程序,因此被init接管的所有程序都不會變成殭屍程序。

二、unix下程序的運作方式

每個unix程序在程序表裡都有乙個進入點(entry),核心程序

執行該程序時使用到的一切資訊都儲存在進入點。當用 ps 命令察看系統中的程序資訊時,看到的就是程序表中的相關資料。當以fork()系統呼叫建立乙個新的程序後,核心程序就會在程序表中給這個新程序分配乙個進入點,然後將相關資訊儲存在該進入點所對應的程序表內。這些資訊中有一項是其父程序的識別碼。

子程序的結束和父程序的執行是乙個非同步過程,即父程序永遠無法**子程序

到底什麼時候結束。那麼會不會因為父程序太忙來不及 wait 子程序,或者說不知道

子程序什麼時候結束,而丟失子程序結束時的狀態資訊呢?

不會。因為unix提供了一種機制可以保證,只要父程序想知道子程序結束時的狀態資訊,

就可以得到。這種機制就是:

當子程序走完了自己的生命週期後,它會執行exit()系統呼叫

,核心釋放該程序所有的資源,包括開啟的檔案,占用的記憶體等。

但是仍然為其保留一定的資訊(包括程序號the process id,

退出碼exit code,

退出狀態the termination

status of the process,執行時間the amount of cpu time taken by the process等),

這些資料會一直保留到系統將它傳遞給它的父程序為止,

直到父程序通過wait / waitpid來取時才釋放。

也就是說,

當乙個程序死亡時,它並不是完全的消失了。程序終止,它不再執行,但是還有一些殘留的資料等待父程序收回。當父程序 fork() 乙個子程序後,它必須用 wait() (或者 waitpid())等待子程序退出。正是這個 wait() 動作來讓子程序的殘留資料消失。

三、殭屍程序的危害

如果父程序不呼叫wait / waitpid的話,那麼保留的那段資訊就不會釋放,其程序號就會一直被占用,但是系統的程序表容量是有限的,所能使用的程序號也是有限的,如果大量的產生殭屍程序,將因為沒有可用的程序號而導致系統不能產生新的程序。

所以,defunct程序不僅占用系統的記憶體資源,影響系統的效能,而且如果其數目太多,還會導致系統癱瘓。而且,

由於排程程式無法選中defunct 程序,所以不能用kill命令刪除defunct 程序,惟一的方法只有重啟系統。

四、殭屍程序的產生

如果子程序死亡時父程序沒有 wait(),通常用 ps 可以看到它被顯示為「」,這樣就產生了殭屍程序。它將永遠保持這樣直到父程序 wait()。

由此可見,defunct程序的出現時間是在子程序終止後,但是父程序尚未讀取這些資料之前。利用這一點我們可以用下面的程式建立乙個defunct 程序:

c**  

#include 

#include

main()  

sleep(20);  

printf(「parent pid=%d \n」, getpid());  

exit(0);  

}    

當上述程式以後臺的方式執行時,第17行強迫程式睡眠20秒,讓使用者有時間輸入ps -e指令,觀察程序的狀態,我們看到程序表中出現了defunct程序。當父程序執行終止後,再用ps -e命令觀察時,我們會發現defunct程序也隨之消失。這是因為父程序終止後,init 程序會接管父程序留下的這些「孤兒程序」(orphan process),而這些「孤兒程序」執行完後,它在程序表中的進入點將被刪除。如果乙個程式設計上有缺陷,就可能導致某個程序的父程序一直處於睡眠狀態或是陷入死迴圈,父程序沒有wait子程序,也沒有終止以使init接管,該子程序執行結束後就變成了defunct程序,這個defunct 程序可能會一直留在系統中直到系統重新啟動。

在看乙個產生殭屍程序的例子。

子程序要執行的程式test_prog

c**  

#include 

int main()  

return 0;  

}  

父程序father的**father.c

c**  

#include 

#include 

#include 

#include 

int main()  

else

*/while (1)  

return 0;  

}  }  

執行./father,當子程序退出後,由於父程序沒有對它的退出進行關注,會出現殭屍程序

c**  

20786 pts/0    00:00:00 father  

20787 pts/0    00:00:00 father 

總結:子程序成為 defunct 直到父程序 wait(),除非父程序忽略了 sigcld 。更進一步,父程序沒有 wait() 就消亡(仍假設父程序沒有忽略 sigcld )的子程序(活動的或者 defunct)成為 init 的子程序,init 著手處理它們。

五、如何避免殭屍程序

1、父程序通過wait和waitpid等函式等待子程序結束,這會導致父程序掛起。

2. 如果父程序很忙,那麼可以用signal函式為sigchld安裝handler。在子程序結束後,父程序會收到該訊號,可以在handler中呼叫wait**。

3. 如果父程序不關心子程序什麼時候結束,那麼可以用

signal(sigcld, sig_ign)或

signal(sigchld, sig_ign)通知核心,自己對子程序的結束不感興趣,那麼子程序結束後,核心會**,並不再給父程序傳送訊號 

4. fork兩次,父程序fork乙個子程序,然後繼續工作,子程序fork乙個孫程序後退出,那麼孫程序被init接管,孫程序結束後,init會**。不過子程序的**還要自己做。 下面就是stevens給的採用兩次folk避免殭屍程序的示例:

c**  

#include "apue.h"

#include 

intmain(void)  

... else

if (pid == 0) ...  

if (waitpid(pid, null, 0) != pid)  /**/

/* wait for first child */

err_sys("waitpid error");  

/**/

/** we're the parent (the original process); we continue executing,

* knowing that we're not the parent of the second child.

*/exit(0);  

}  

殭屍程序和處理方式

在父程式中用於判斷子程式是否結束 殭屍程序 子程序提前于父程序結束,造成無法清理掉pcb和其他系統消耗造成pid的長時間占用 如上圖 如果父程序異常結束時就成了乙個孤兒程序交個pid 1 的init作為父程序進行管理收屍 三種處理方式 父程序通過wait和waitpid等函式等待子程序結束,這會導致...

Defunct程序 殭屍程序

下面談談 defunct 程序,中文翻譯叫殭屍程序。下文整理於網路以及apue一書。一 什麼是殭屍程序 在unix 系統中,乙個程序結束了,但是他的父程序沒有等待 呼叫wait waitpid 他,那麼他將變成乙個殭屍程序。當用ps命令觀察程序的執行狀態時,看到這些程序的狀態列為defunct。殭屍...

Defunct 程序 殭屍程序

下面談談 defunct 程序,中文翻譯叫殭屍程序。下文整理於網路以及apue一書。一 什麼是殭屍程序 在unix 系統中,乙個程序結束了,但是他的父程序沒有等待 呼叫wait waitpid 他,那麼他將變成乙個殭屍程序。當用ps命令觀察程序的執行狀態時,看到這些程序的狀態列為defunct。殭屍...