殭屍進程與孤兒進程解析

我們都知道Linux中的進程可以分有多種狀態:

R(runing):運行狀態

S(sleeping):淺度睡眠狀態

D(disk sleep):磁盤睡眠狀態

T(stopped):停止進程

X(dead):死亡狀態

Z(zombie):殭屍狀態


殭屍狀態

(1)殭屍狀態是一個比較特殊的狀態,當進程退出父進程(使用wait()系統調用)沒有讀取到子進程退出的返回代碼時就會產生殭屍進程。殭屍進程會在以終止狀態保持在進程表中,並且會一直等待父進程讀取退出狀態代碼

運行結果:


 我們知道殭屍進程的產生是因爲進程退出時父進程並沒有等待子進程,也就是說,父進程和子進程是不同步的。
那麼問題來了,父進程是不會預測到子進程退出的,也就不會第一時間去處理,所以,Linux爲了防止子進程丟失退出時的狀態信息,而產生了殭屍進程。也就是說,子進程雖然退出釋放資源,然而仍有一部分資源等着父進程來釋放,父進程來釋放之前一直都是佔用着內存的,那麼,如果有很多的殭屍進程呢?那還不卡死。

子進程進程號會一直被佔用,但是系統所能使用的進程號是有限的,如果大量的產生僵死進程,將因爲沒有可用的進程號而導致系統不能產生新的進程。

那麼如何避免這個問題呢?
⒈父進程通過wait和waitpid等函數等待子進程結束,這會導致父進程掛起。
⒉ 如果父進程很忙,那麼可以用signal函數爲SIGCHLD安裝handler,因爲子進程結束後, 父進程會收到該信號,可以在handler中調用wait回收。
⒊ 如果父進程不關心子進程什麼時候結束,那麼可以用signal(SIGCHLD,SIG_IGN) 通知內核,自己對子進程的結束不感興趣,那麼子進程結束後,內核會回收, 並不再給父進程發送信號。
⒋ 還有一些技巧,就是fork兩次,父進程fork一個子進程,然後繼續工作,子進程fork一 個孫進程後退出,那麼孫進程被init接管,孫進程結束後,init會回收。不過子進程的回收 還要自己做。


孤兒進程

一個父進程退出,而他的一個或者多個子進程還在運行,那麼那些子進程將成爲孤兒進程。孤兒進程將被init進程(進程號爲1)所收養,並由init進程對他們完成狀態收集工作。

運行結果:


     任何一個子進程(init除外)在exit()之後,並非馬上就消失掉,而是留下一個稱爲殭屍進程(Zombie)的數據結構,等待父進程處理。這是每個子進程在結束時都要經過的階段。如果子進程在exit()之後,父進程沒有來得及處理,這時用ps命令就能看到子進程的狀態是“Z”。如果父進程能及時處理,可能用ps命令就來不及看到子進程的殭屍狀態,但這並不等於子進程不經過殭屍狀態。  如果父進程在子進程結束之前退出,則子進程將由init接管。init將會以父進程的身份對子進程進行處理。
      這樣來看,孤兒進程並不會有什麼危害,真正會對系統構成威脅的是僵死進程。例如有這樣一個父進程:它定期的產生一個子進程,這個子進程做完它該做的事情之後就退出了,因此這個子進程的生命週期很短,但是,父進程只管生成新的子進程,至於子進程退出之後的事情,則一概不聞不問,這樣,系統運行上一段時間後,系統中就會存在很多的僵死進程,倘若用ps命令查看的話,就會看到很多狀態爲Z的進程。 嚴格地來說,僵死進程並不是問題的根源,罪魁禍首是產生出大量僵死進程的那個父進程。因此,當我們需要消滅系統中大量的僵死進程時, 就要把產生大量僵死進程的那個殺掉(也就是通過kill發送SIGTERM或者SIGKILL信號)。槍斃了元兇進程之後,它產生的僵死進程就變成了孤兒進程,這些孤兒進程會被init進程接管,init進程會wait()這些孤兒進程,釋放它們佔用的系統進程表中的資源 。

殭屍進程與孤兒進程的區別:
孤兒進程是子進程還在運行,而父進程掛了,子進程被init進程收養。殭屍進程是父進程還在運行但是子進程掛了,但是父進程卻沒有使用wait來清理子進程的進程信息,導致子進程雖然運行實體已經消失,但是仍然在內核的進程表中佔據一條記錄,這樣長期下去對於系統資源是一個浪費。殭屍進程將會導致資源浪費,而孤兒則不會。

發佈了85 篇原創文章 · 獲贊 148 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章