Unix中的殭屍進程

現實中的殭屍是一種似死非死的狀態——一直在人間四處遊走,但是卻不能做一些活人能做的事情。


而對於殭屍進程,與這個有些類似,它不能像一般進程那樣來執行程序、被調度而佔用CPU,但是它卻不能消失,因爲它還沒將它的“死訊”告訴它的父進程和系統,它得讓別人知道它已經死了,免得別人還來找它幹活它卻啥都不能幹。


我們需要瞭解到,進程的終止是一件異步事件,即是一件可能隨時都會發生的事情。進程有8種終止方式,包括5種正常終止方式和3種非正常終止方式。當一個進程終止的時候,當然,我們的內核肯定是會知道這件事情的(因爲這幾種終止方式總是離不開信號或者系統調用)。當內核發現有進程終止時,它將向終止進程的父進程發送一個SIGCHLD信號,告訴該終止進程的父進程,你的幾號子進程終止了。當然,子進程終止了,它肯定想要告訴父進程它的“死因”是什麼,這裏的"死因"就是子進程的終止狀態。如果子進程是正常終止,則有子進程自己將終止狀態返回給父進程;如果子進程是非正常終止的,還沒來得及傳送終止狀態,則由內核(不是子進程本身)產生一個指示其異常終止原因的終止狀態。而我們的父進程獲取這些終止狀態也要主動的,是通過主動調用wait()或者waitpid()函數來獲得的,當然這兩個函數也將獲取到子進程死前的一些信息,包括其進程ID,其使用的CPU時間總量等。那麼問題來了,我們的父進程是什麼時候調用wait()或者waitpid()來獲取這些終止狀態的呢?是在子進程死後(內核發送SIGCHLD信號),還是在子進程沒死的時候呢?當然,在死後調用函數纔是正確的,如果在進程還沒有終止的時候就調用函數則會發生什麼呢?這個問題大了,我們(子進程)都還好好的,你(父進程)就來求死訊,這不是咒我們嗎?好吧,你這麼壞,就罰你不準動,在我們還活着的這段時間裏都好好等着吧。也就是說,父進程在所有進程都在運行的時候就調用函數,則會導致父進程陷入阻塞狀態,直到有一個子進程終止,父進程才能被再次調度。如果是在子進程終止後調用函數,則就可以順利取得該終止子進程的終止狀態和一些終止信息然後立即返回,在這裏,父進程調用函數的最好地點就是在SIGCHLD信號的處理函數中了。最後還有一種情況,如果一個進程沒有子進程,那麼調用wait()等函數則會出錯返回。


好了,說了這麼多也沒有說到主題上,到現在都還沒有提到殭屍進程是啥。囧,實在是細節太多,寫着寫着就容易跑偏了。好了,從上面瞭解了進程終止時發生的一些事情,我們可以比較清楚地瞭解到,父進程是通過顯式調用wait()或者waitpid()來獲取終止子進程的終止狀態信息的,通俗點說就是,父進程是得顯式地調用一個函數來爲我們已經終止的子進程"收屍"的,如果父進程沒有顯式調用這些函數,會導致父進程不能在子進程終止後的第一時間內就將其"收屍",這就導致在子進程死後有一段時間是以終止的狀態存在在系統中的,這種狀態的子進程我們成爲"殭屍進程",而這個時間段就是"子進程死後到父進程顯式調用wait等函數獲取終止子進程終止狀態信息"的時間段。好吧,應該瞭解殭屍進程的含義了吧,下面通過一個示例來展示一下殭屍進程。


代碼和運行界面如下:




如上面的代碼,我們創建了一個子進程,然後讓父進程sleep(),讓子進程先執行。在子進程裏面直接終止自身,即這個效果的過程應該是,在父進程運行的時候,子進程已經終止了,並且沒有調用wait()或者waitpid()函數來獲取終止子進程的終止狀態和信息,於是在子進程終止後父進程終止前的這段時間裏,子進程是一個殭屍進程。我們可以從另外一個終端裏面使用ps命令查看當前系統中的殭屍進程,查看結果如下:




從上面的運行結果可以看出,系統中存在一個殭屍進程,就是我們剛剛創建的子進程。


在上面的程序中,我們編寫了sleep函數使得子進程先於父進程執行完,但是究竟是哪個進程先執行,這個與系統的調度算法相關。所以有種情況可能會發生——父進程先於子進程終止。要知道,上面說的那麼多,都是要靠父進程才能完成的,現在父進程先終止了,誰來爲子進程的終止善後呢?現實生活中沒有了雙親的孩子就是孤兒了,我們姑且將這種子進程成爲孤兒進程吧。孤兒一般都是由孤兒院收養,孤兒院的院長就是他們的繼父繼母了。在內核中也有個"孤兒院院長進程",就是init進程,這個進程會收養內核中所有沒有父進程的孤兒進程,成爲它們的父進程,這樣,就可以保證內核中所有的進程都有父進程了。inti進程是如何收養的呢?其實init進程一直都很忙,只要內核中有進程終止,init進程都要去查看一下這個終止進程是否還存在子進程,如果有的話就收養它們,成爲它們的新父進程。另外還有最後一點,init進程有些不一樣,它不像普通父進程那麼磨蹭,慢慢地去調用wait來獲取終止子進程狀態和信息,init進程帥氣多了,每當內核中有進程終止,init進程都會調用wait函數取得其終止狀態,這樣init進程真的很忙,已有進程終止,既要領養終止進程的子進程,又要調用wait來獲取其終止狀態,但是它這麼做卻是保證了內核中的孤兒進程都有父進程,別且防止了內核中被塞滿了殭屍進程。nice。

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