Linux中的 殭屍進程 和 孤兒進程

UNIX裏,除了進程0(即PID=0的交換進程,Swapper Process)以外的所有進程都是由其他進程使用系統調用fork創建的,這裏調用fork創建新進程的進程即爲父進程,而相對應的爲其創建出的進程則爲子進程,因而除了進程0以外的進程都只有一個父進程,但一個進程可以有多個子進程。

操作系統內核進程標識符Process Identifier,即PID)來識別進程。進程0是系統引導時創建的一個特殊進程,在其調用fork創建出一個子進程(即PID=1的進程1,又稱init)後,進程0就轉爲交換進程(有時也被稱爲空閒進程),而進程1(init進程)就是系統裏其他所有進程的祖先。

殭屍進程與孤兒進程

當一個子進程結束運行(一般是調用exit、運行時發生致命錯誤或收到終止信號所導致)時,子進程的退出狀態(返回值)會回報給操作系統,系統則以SIGCHLD信號將子進程被結束的事件告知父進程,此時子進程的進程控制塊(PCB)仍駐留在內存中。一般來說,收到SIGCHLD後,父進程會使用wait系統調用以取得子進程的退出狀態,然後內核就可以從內存中釋放已結束的子進程的PCB;而如若父進程沒有這麼做的話,子進程的PCB就會一直駐留在內存中,也即成爲殭屍進程

孤兒進程則是指父進程結束後仍在運行的子進程。在類UNIX系統中,孤兒進程一般會被init進程所“收養”,成爲init的子進程。

爲避免產生殭屍進程,實際應用中一般採取的方式是:

  1. 將父進程中對SIGCHLD信號的處理函數設爲SIG_IGN(忽略信號);
  2. fork兩次並殺死一級子進程,令二級子進程成爲孤兒進程而被init所“收養”、清理。 
==================以上來自維基百科http://zh.wikipedia.org/wiki/%E7%88%B6%E8%BF%9B%E7%A8%8B===============


Unix編程中所謂"殭屍進程"指什麼,什麼情況下會產生殭屍進程,如何殺掉殭屍進程


fork()/execve()過程中,假設子進程結束時父進程仍存在,而父進程fork()之前既沒安裝SIGCHLD信號處理函數調用wait()waitpid()等待子進程結束,又沒有顯式忽略該信號,則子進程成爲殭屍進程,無法正常結束。此時即使是root身份kill -9也不能殺死殭屍進程(僵死進程實際上是已死的進程,你當然不能殺死一個死人)。補救辦法是殺死殭屍進程的父進程(殭屍進程的父進程必然存在),殭屍進程成爲"孤兒進程",過繼給1號進程init,init始終會負責清理殭屍進程。

●在一個進程調用了exit之後,該進程並非馬上就消失掉,而是留下一個稱爲殭屍進程(Zombie)的數據結構。在Linux進程的5種狀態中,殭屍進程是非常特殊的一種,它已經放棄了幾乎所有內存空間,沒有任何可執行代碼,也不能被調度,僅僅在進程列表中保留一個位置,記載該進程的退出狀態等信息供其他進程收集(如供父進程),除此之外,殭屍進程不再佔有任何內存空間。從這點來看,殭屍進程雖然有一個很酷的名字,但它的影響力遠遠抵不上那些真正的殭屍兄弟,真正的殭屍總能令人感到恐怖,而殭屍進程卻除了留下一些供人憑弔的信息,對系統毫無作用。

系統調用exit,它的作用是使進程退出,但也僅僅限於將一個正常的進程變成一個殭屍進程,並不能將其完全銷燬。殭屍進程雖然對其他進程幾乎沒有什麼影響,不佔用CPU時間,消耗的內存也幾乎可以忽略不計,但有它在那裏呆着,還是讓人覺得心裏很不舒服。而且Linux系統中進程數目是有限制的,在一些特殊的情況下,如果存在太多的殭屍進程,也會影響到新進程的產生。

●如何清理殭屍進程:wait()waitpid()系統調用可以清理殭屍進程,或者殺死殭屍進程的父進程(殭屍進程的父進程必然存在),殭屍進程成爲"孤兒進程",過繼給1號進程initinit始終會負責清理殭屍進程。

下面這段比喻形容了進程的一生,也更容易看出殭屍進程所處的階段:

隨着一句fork,一個新進程呱呱落地,但它這時只是老進程的一個克隆。

然後隨着exec,新進程脫胎換骨,離家獨立,開始了爲人民服務的職業生涯。

人有生老病死,進程也一樣,它可以是自然死亡,即運行到main函數的最後一個"}",從容地離我們而去;也可以是自殺,自殺有2種方式,一種是調用exit函數,一種是在main函數內使用return,無論哪一種方式,它都可以留下遺書,放在返回值裏保留下;它還甚至能可被謀殺,被其它進程通過另外一些方式結束他的生命。

進程死掉以後,會留下一具殭屍,waitwaitpid充當了殮屍工,把殭屍推去火化,使其最終歸於無形。

這就是進程完整的一生。


殭屍進程的危害

 由於子進程的結束和父進程的運行是一個異步過程,即父進程永遠無法預測子進程 到底什麼時候結束. 那麼不會因爲父進程太忙來不及wait子進程,或者說不知道子進程什麼時候結束,而丟失子進程結束時的狀態信息呢? 不會.因爲UNIX提供了一種機制可以保證 只要父進程想知道子進程結束時的狀態信息, 就可以得到. 這種機制就是:在每個進程退出的時候,內核釋放該進程所有的資源,包括打開的文件,佔用的內存等. 但是仍然爲其保留一定的信息(包括進程號the process ID,退出狀態the termination status of the process,運行時間the amount of CPU time taken by the process等), 直到父進程通過wait / waitpid來取時才釋放. 但這樣就導致了問題,如果你進程不調用wait / waitpid的話, 那麼保留的那段信息就不會釋放,其進程號就會一定被佔用,但是系統所能使用的進程號是有限的,如果大量的產生僵死進程,將因爲沒有可用的進程號而導致系統不能產生新的進程. 此即爲殭屍進程的危害應當避免。


處理:

*顯式忽略SIGCHLD信號是指類似這樣的代碼:

signal( SIGCHLD, SIG_IGN );

*安裝SIGCHLD信號句柄是指類似這樣的代碼:


當然,不建議使用signal(),應該使用sigaction()


殭屍進程中保存着很多對程序員和系統管理員非常重要的信息,首先,這個進程是怎麼死亡的?是正常退出呢,還是出現了錯誤,還是被其它進程強迫退出的?其次,這個進程佔用的總系統CPU時間和總用戶CPU時間分別是多少?發生頁錯誤的數目和收到信號的數目。這些信息都被存儲在殭屍進程中,試想如果沒有殭屍進程,進程一退出,所有與之相關的信息都立刻歸於無形,而此時程序員或系統管理員需要用到,就只好乾瞪眼了。

那麼,我們如何收集這些信息,並終結這些殭屍進程呢?就要靠waitpid調用和wait調用。這兩者的作用都是收集殭屍進程留下的信息,同時使這個進程徹底消失。

pid_t wait(int *status)

1進程一旦調用了wait,就立即阻塞自己,由wait自動分析是否當前進程的某個子進程已經退出,如果讓它找到了這樣一個已經變成殭屍的子進程,wait就會收集這個子進程的信息,並把它徹底銷燬後返回;如果沒有找到這樣一個子進程,wait就會一直阻塞在這裏,直到有一個出現爲止。

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

pid = wait(NULL);

如果成功,wait會返回被收集的子進程的進程ID,如果調用進程沒有子進程,調用就會失敗,此時wait返回-1,同時errno被置爲ECHILD

參數status

如果參數status的值不是NULLwait就會把子進程退出時的狀態取出並存入其中,這是一個整數值(int),指出了子進程是正常退出還是被非正常結束的(一個進程也可以被其他進程用信號結束),以及正常結束時的返回值,或被哪一個信號結束的等信息。由於這些信息被存放在一個整數的不同二進制位中,所以用常規的方法讀取會非常麻煩,人們就設計了一套專門的宏(macro)來完成這項工作,下面我們來學習一下其中最常用的兩個:

● WIFEXITED(status)

這個宏用來指出子進程是否爲正常退出的,如果是,它會返回一個非零值。

(請注意,雖然名字一樣,這裏的參數status並不同於wait唯一的參數--指向整數的指針status,而是那個指針所指向的整數,切記不要搞混了。)

● WEXITSTATUS(status)

WIFEXITED返回非零值時,我們可以用這個宏來提取子進程的返回值,如果子進程調用exit(5)退出,WEXITSTATUS (status)就會返回5;如果子進程調用exit(7)WEXITSTATUS(status)就會返回7。請注意,如果進程不是正常退出的,也就是說,WIFEXITED返回0,這個值就毫無意義。


Linux中的進程基本狀態:

    1、執行(R)狀態:CPU正在執行,即進程正在佔用CPU

    2、就緒(W)狀態:進程已經具備的執行的一切條件,正在等待分配CPU的處理時間片。

    3、停止(S)狀態:進程不能使用CPU


三個函數:

fork();功能:創建一個新的進程。(fork()<0[出錯]fork()==0[子進程]fork()>0[父進程]

wait();功能:真正結束進程(收屍)。

exec();功能:執行外部程序。


孤兒進程

孤兒進程:一個父進程退出,而它的一個或多個子進程還在進行,那麼那些子進程將成了孤兒進程,孤兒進程將被init進程(進程號爲1)所收養,並由init進程對它們完成狀態收集工作。殭屍資源會造成資源浪費,孤兒進程則不會。



鏈接:  

http://www.cnblogs.com/mydomain/archive/2010/10/24/1859826.html

http://gengning938.blog.163.com/blog/static/12822538120116188816625/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章