宋寶華: 殭屍進程的成因以及殭屍可以被「殺死」嗎?

  • 2019 年 11 月 10 日
  • 筆記

殭屍不可能被殺死,因為它已經死了,不存在再死一次的問題。死的對立面是活,死者已死。只有活的進程才可能被殺死。

什麼是殭屍

首先要明確一點,殭屍進程的含義是:子進程已經死了,但是父進程還沒有wait它的一個中間狀態,這個時候子進程是一個殭屍。正常情況下子死,父wait,清理掉子進程的task_struct,釋放子進程的PID:

編譯上述程式,運行,我們看到2個a.out進程:

殺死子進程4578,看到父進程的列印:

之後,4578會消失,因為父進程執行到了wait,也知道了子進程是被訊號2殺掉的。

但是如果子進程死了,父進程不執行到wait,比如把上圖中的"#if 0"改為"#if 1",殺死子進程後,子進程就會是一個殭屍:

我們重新運行,當我們用kill -2殺掉子進程4628後,我們發現4628成為一個殭屍,狀態變為Z+,名字上也加了一個棺材[],成為[a.out]:

殭屍不可能被殺死

我們看到上面4628是個殭屍很不爽,所以我們想把它幹掉,據說Linux有個訊號9,神擋殺神,佛擋殺佛,我們現在來用kill -9幹掉4628:

從上圖可以看出,我們把4628用kill -9捅了好多刀,但是最後看4628這個殭屍,還是沒有消失。

因為殭屍已經是死了,它不可能再次被殺死,你給它捅一萬刀,它也是個死人,不可能再次死!

殭屍不可能被殺死,因為它已經死了!只等父進程來wait清理屍體了。

這個時候我們能夠把殭屍消失掉的方法,就是殺死殭屍進程的父進程4627。

一個殭屍可以被殺死的假象

下面的這個程式證明「殭屍可以被殺死」

我們在主執行緒裡面,pthread_create()創建執行緒後,pthread_exit()退出,這個時候我們會發現,在ps命令裡面,a.out顯示為一個殭屍:

這個時候我們來殺死4730這個殭屍:

kill -9 4730

我們會驚奇地發現,4730真地會從ps命令裡面消失!

我們把時間軸拉回調用"kill -9 4730"之前。剛才我們「看起來」能殺死殭屍的本質原因是,當主執行緒4730調用pthread_exit()退出後,主執行緒4730的狀態確實是殭屍了,但是該進程裡面的4731執行緒,卻沒有死:

看看4731:

4731是活著的,證明整個進程並沒有掛。所以4730的退出,只是讓整個進程半死。而由於ps這些命令的誤會,4730湊巧又是整個進程的PID,它顯示地好像整個4370成了殭屍一樣。

那麼,根據POSIX標準關於訊號(signal)的定義,當我們執行kill -9 4730(4730是4730和4731的TGID,也是整個進程用戶態視角的PID)的時候,是要殺死整個4730進程的,所以這個時候4731被我們殺死,整個進程就都死了,這個時候,執行到父進程的wait邏輯,導致殭屍消失。

所以,在本例中,kill -9 4730看起來是"殺死了殭屍」,實際是殺死了4730整個進程(裡面的每個執行緒),導致整個進程死。在次之前,整個進程實際還是活的。