在unix 系統中,乙個程序結束了,但是他的父程序沒有等待(呼叫wait / waitpid)
他, 那麼他將變成乙個殭屍程序. 但是如果該程序的父程序已經先結束了,那麼
該程序就不會變成殭屍程序, 因為每個程序結束的時候,系統都會掃瞄當前系統
中所執行的所有程序, 看有沒有哪個程序是剛剛結束的這個程序的子程序,如果
是的話,就由init 來接管他,成為他的父程序……
乙個程序在呼叫exit命令結束自己的生命的時候,其實它並沒有真正的被銷毀, 而是留
下乙個稱為殭屍程序(zombie)的資料結構(系統呼叫exit,它的作用是 使程序退出,
但也僅僅限於將乙個正常的程序變成乙個殭屍程序,並不能將其完全銷毀)
2. 殭屍程序的危害
由於子程序的結束和父程序的執行是乙個非同步過程,即父程序永遠無法**子程序 到底
什麼時候結束. 那麼不會因為父程序太忙來不及waid子程序,或者說不知道 子程序什麼
時候結束,而丟失子程序結束時的狀態資訊呢? 不會.因為unix提供了一種機制可以保
證 只要父程序想知道子程序結束時的狀態資訊, 就可以得到. 這種機制就是: 在每個進
程退出的時候,核心釋放該程序所有的資源,包括開啟的檔案,占用的記憶體等. 但是仍然為
其保留一定的資訊(包括程序號the process id,退出狀態the termination status of the
process,執行時間the amount of cpu time taken by the process等), 直到父程序通過
wait / waitpid來取時才釋放. 但這樣就導致了問題,如果你程序不呼叫wait / waitpid的話,
那麼保留的那段資訊就不會 釋放,其程序號就會一定被占用,但是系統所能使用的程序號
是有限的,如果大量的產生 僵死程序,將因為沒有可用的程序號而導致系統不能產生新的
程序. 此即為殭屍程序的危害,應當避免.
3.殭屍程序的避免
1、父程序通過wait和waitpid等函式等待子程序結束,這會導致父程序掛起
2. 如果父程序很忙,那麼可以用signal函式為sigchld安裝handler,因為子程序結
束后, 父程序會收到該訊號,可以在handler中呼叫wait**
3. 如果父程序不關心子程序什麼時候結束,那麼可以用signal(sigchld, sig_ign)
通知核心,自己對子程序的結束不感興趣,那麼子程序結束後,核心會**, 並不再給
父程序傳送訊號
4. 還有一些技巧,就是fork兩次,父程序fork乙個子程序,然後繼續工作,子程序fork
一 個孫程序後退出,那麼孫程序被init接管,孫程序結束後,init會**。不過子程序
的** 還要自己做。 下面就是stevens給的採用兩次folk避免殭屍程序的示例.
3、殭屍程序的處理: 它需要它的父程序來為它收屍,如果他的父程序沒安裝sigchld
訊號處理函式呼叫wait或waitpid()等待子程序結束,又沒有顯式忽略該訊號,那麼它就
一直保持殭屍狀態; 存在的問題:如果父程序是乙個迴圈,不會結束,那麼子程序就會
一直保持殭屍狀態,這就是為什麼系統中有時會有很多的殭屍程序,系統的效能可能會
收到影響。 ** 如果這時父程序結束了,那麼init程序自動會接手這個子程序,為它收
屍,它還是能被清除的。 4、子程序結束後為什麼要進入殭屍狀態? * 因為父程序可能
要取得子程序的退出狀態等資訊。 5、殭屍狀態是每個子程序比經過的狀態嗎? 是的。
* 任何乙個子程序(init除外)在exit()之後,並非馬上就消失掉,而是留下乙個稱為殭屍
程序(zombie)的資料結構,等待父程序處理。這是每個 子程序在結束時都要經過的階段。
如果子程序在exit()之後,父程序沒有來得及處理,這時用ps命令就能看到子程序的狀 態是「z」。如果父程序能及時 處理,可能用ps命令就來不及看到子程序的殭屍狀態,但
這並不等於子程序不經過殭屍狀態。 * 如果父程序在子程序結束之前退出,則子程序將
由init接管。init將會以父程序的身份對殭屍狀態的子程序進行處理。 6、如何檢視殭屍
程序: $ ps -el 其中,有標記為z的程序就是殭屍程序 s代表休眠狀態;d代表不可
中斷的休眠狀態;r代表執行狀態;z代表僵死狀態;t代表停止或跟蹤狀態
wait 系統呼叫
系統中的殭屍程序都要由wait系統呼叫來**,下面就通過實戰看一看wait的具體用法:
wait的函式原型是:
#include
#include
pid_t wait(int *status);
程序一旦呼叫了wait,就立即阻塞自己,由wait自動分析是否當前程序的某個子程序已經
退出,如果讓它找到了這樣乙個已經變成殭屍的子程序, wait就會收集這個子程序的資訊,
並把它徹底銷毀後返回;如果沒有找到這樣乙個子程序,wait就會一直阻塞在這裡,直到有
乙個出現為止。
引數status用來儲存被收集程序退出時的一些狀態,它是乙個指向int型別的指標。但如果
我們對這個子程序是如何死掉的毫不在意,只想把這個殭屍程序消滅掉,(事實上絕大多數
情況下,我們都會這樣想),我們就可以設定這個引數為null,就象下面這樣:
pid = wait(null);
如果成功,wait會返回被收集的子程序的程序id,如果呼叫程序沒有子程序,呼叫就會失
敗,此時wait返回-1,同時errno被置為echild。
下面就讓我們用乙個例子來實戰應用一下wait呼叫:
#include
#include
#include
#include
#include
#include
int main()
else if ( pc == 0)
else
exit(0);
} 編譯並執行:
$ gcc wait1.c -o wait1
$ ./wait1
i am child process with pid 2351
now in parent process, pid = 2350
i am waiting child process to exit.
i catched a child process with pid of 2351
可以明顯注意到,在第2行結果列印出來前有10秒鐘的等待時間,這就是我們設定的讓子
程序睡眠的時間,只有子程序從睡眠中甦醒過來,它才能正常退 出,也就才能被父程序捕
捉到。其實這裡我們不管設定子程序睡眠的時間有多長,父程序都會一直等待下去,讀者如
果有興趣的話,可以試著自己修改一下這個數 值,看看會出現怎樣的結果。
如果引數status的值不是null,wait就會把子程序退出時的狀態取出並存入其中,這是一
個整數值(int),指出了子程序是正常退出還是 被非正常結束的(乙個程序也可以被其他
程序用訊號結束,我們將在以後的文章中介紹),以及正常結束時的返回值,或被哪乙個信
號結束的等資訊。由於這些資訊 被存放在乙個整數的不同二進位制位中,所以用常規的方法
讀取會非常麻煩,人們就設計了一套專門的巨集(macro)來完成這項工作,下面我們來學習 一下其中最 常用的兩個:
1,wifexited(status) 這個巨集用來指出子程序是否為正常退出的,如果是,它會返回乙個
非零值。
(請注意,雖然名字一樣,這裡的引數status並不同於wait唯一的引數–指向整數的指標
status,而是那個指標所指向的整數,切記不要搞混了。)
2, wexitstatus(status) 當wifexited返回非零值時,我們可以用這個巨集來提取子進
程的返回值,如果子程序呼叫exit(5)退出,wexitstatus(status) 就會返回5;如果子程序
呼叫exit(7),wexitstatus(status)就會返回7。請注意,如果程序不是正常退出的,也就
是說, wifexited返回0,這個值就毫無意義。
下面通過例子來實戰一下我們剛剛學到的內容:
#include
#include
#include
int main()
else
else
printf("the child process %d exit abnormally.\n", pr);
}
exit(0);
} 編譯並執行:
$ gcc wait2.c -o wait2
$ ./wait2
this is child process with pid of 1538.
the child process 1538 exit normally.
the return code is 3.
父程序準確捕捉到了子程序的返回值3,並把它列印了出來。
當然,處理程序退出狀態的巨集並不止這兩個,但它們當中的絕大部分在平時的程式設計中很少用
到,就也不在這裡浪費篇幅介紹了,有興趣的讀者可以自己參閱linux man pages去了解它
們的用法。
殭屍程序與孤兒程序
殭屍程序 如果乙個程序通過fork 建立子了乙個程序,當子程序退出而父程序還沒有呼叫wait waitpid來儲存子程序的狀態且 它,此時子程序的狀態描述資訊仍然保留在系統中,稱這類程序為殭屍程序 殭屍程序的優點是方便上層程序得知子程序的退出狀態,但是如果不及時 會造成記憶體洩露 孤兒程序 當乙個父...
殭屍程序與孤兒程序
孤兒程序是指在程序還未退出之前它的父程序就已經先退出了,簡單來說就是乙個沒有父程序的子程序就是孤兒程序。既然所有的程序都必須在退出之後被父程序的wait 或waitpid 以釋放其遺留在系統中的一些資源,那麼孤兒程序的這些東西又誰來處理呢?這個任務就落到了init程序的身上,init程序就好像乙個收...
精靈程序與殭屍程序
精靈程序 精靈程序 d a e m o n 是生存期長的一種程序。它們常常在系統引導裝入時起動,在系統關閉時終止。因為它們沒有控制終端,所以說它們是在後台執行的。所有精靈程序都以超級使用者 使用者 i d為0 的優先權執行。殭屍程序 在unix 系統中,乙個程序結束了,但是他的父程序沒有等待 呼叫w...