定義:
In UNIX System terminology, a process that has terminated,but whose parent has not yet waited for it, is called a zombie.
在UNIX系統中,一個進程結束了,但是他的父進程沒有等待(調用wait / waitpid)他, 那麼他將變成一個僵屍進程. 在fork()/execve()過程中,假設子進程結束時父進程仍存在,
而父進程fork()之前既沒安裝SIGCHLD信號處理函數調用waitpid()等待子進程結束,又沒有顯式忽略該信號,則子進程成為僵屍進程。
如何查看linux系統上的僵屍進程,如何統計有多少僵屍進程?
#ps -ef | grep defunct
或者查找狀態為Z的進程,Z就是代表zombie process,僵屍進程的意思。
另外使用
top命令查看時有一欄為S,如果狀態為Z說明它就是僵屍進程。
Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie
top命令中也統計了僵屍進程。或者使用下面的命令:
ps -ef | grep defunct | grep -v grep | wc -l
如何殺死僵屍進程呢?
一般僵屍進程很難直接kill掉,不過您可以kill僵屍爸爸。父進程死後,僵屍進程成為”孤兒進程”,過繼給1號進程init,init始終會負責清理僵屍進程.它產生的所有僵屍進程也跟著消失。
ps -e -o ppid,stat | grep Z | cut -d” ” -f2 | xargs kill -9
或
kill -HUP `ps -A -ostat,ppid | grep -e ’^[Zz]‘ | awk ’{print $2}’`
另外子進程死後,會發送SIGCHLD信號給父進程,父進程收到此信號後,執行waitpid()函數為子進程收屍。就是基於這樣的原理:就算父進程沒有調用wait,內核也會向它發送SIGCHLD消息,而此時,盡管對它的默認處理是忽略(?待定,個人感覺默認是不忽略吧!),如果想響應這個消息,可以設置一個處理函數。
如何避免僵屍進程呢?
處理SIGCHLD信號並不是必須的。但對於某些進程,特別是服務器進程往往在請求到來時生成子進程處理請求。如果父進程不等待子進程結束,子進程將成為僵屍進程(zombie)從而占用系統資源。如果父進程等待子進程結束,將增加父進程的負擔,影響服務器進程的並發性能。在
Linux下 可以簡單地將 SIGCHLD信號的操作設為SIG_IGN。
signal(SIGCHLD,SIG_IGN);
這樣,內核在子進程結束時不會產生僵屍進程。這一點與BSD4不同,BSD4下必須顯式等待子進程結束才能釋放僵屍進程
或者
用兩次fork(),而且使緊跟的子進程直接退出,使得孫子進程成為孤兒進程,從而init進程將負責清除這個孤兒進程。
Linux的僵屍進程及其解決方法
1. 產生原因:
在UNIX 系統中,一個進程結束了,但是他的父進程沒有等待(調用wait /waitpid)他,那麼他將變成一個僵屍進程。通過ps命令查看其帶有defunct的標志。僵屍進程是一個早已死亡的進程,但在進程表(processs table)中仍占了一個位置(slot)。
但是如果該進程的父進程已經先結束了,那麼該進程就不會變成僵屍進程。因為每個進程結束的時候,系統都會掃描當前系統中所運行的所有進程,看看有沒有哪個進程是剛剛結束的這個進程的子進程,如果是的話,就由Init進程來接管他,成為他的父進程,從而保證每個進程都會有一個父進程。而Init進程會自動wait其子進程,因此被Init接管的所有進程都不會變成僵屍進程。
2. 原理分析:
每個Unix進程在進程表裡都有一個進入點(entry),核心進程執 行該進程時使用到的一切信息都存儲在進入點。當用 ps 命令察看系統中的進程信息時,看到的就是進程表中的相關數據。當以fork()系統調用建立一個新的進程後,核心進程就會在進程表中給這個新進程分配一個 進入點,然後將相關信息存儲在該進入點所對應的進程表內。這些信息中有一項是其父進程的識別碼。
子進程的結束和父進程的運行是一個異步過程,即父進程永遠無法預測子進程到底什麼時候結束。那麼會不會因為父進程太忙來不及 wait 子進程,或者說不知道子進程什麼時候結束,而丟失子進程結束時的狀態信息呢?不會。因為UNIX提供了一種機制可以保證,只要父進程想知道子進程結束時的狀態信息,就可以得到。這種機制就是:當子進程走完了自己的生命周期後,它會執行exit()系統調用,內核釋放該進程所有的資源,包括打開的文件,占用的內存等。但是仍然為其保留一定的信息(包括進程號the process ID,退出碼exit code,退出狀態the terminationstatus of the process,運行時間the amount of CPU time taken by the process等),這些數據會一直保留到系統將它傳遞給它的父進程為止,直到父進程通過wait / waitpid來取時才釋放。
3.解決方法:
(1) 父進程通過wait和waitpid等函數等待子進程結束,這會導致父進程掛起。
執行wait()或waitpid()系統調用,則子進程在終止後會立即把它在進程表中的數據返回給父進程,此時系統會立即刪除該進入點。在這種情形下就不會產生defunct進程。
(2) 如果父進程很忙,那麼可以用signal函數為SIGCHLD安裝handler。在子進程結束後,父進程會收到該信號,可以在handler中調用wait回收。
(3) 如果父進程不關心子進程什麼時候結束,那麼可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知內核,自己對子進程的結束不感興趣,那麼子進程結束後,內核會回收,並不再給父進程發送信號
(4)fork兩次,父進程fork一個子進程,然後繼續工作,子進程fork一個孫進程後退出,那麼孫進程被init接管,孫進程結束後,init會回收。不過子進程的回收還要自己做。