父進程是init一定不會變僵屍進程嗎?
——lvyilong316
工作中有次操作線上環境時發現有一個“Z”(僵屍)進程,當時首先想到的方法就是把這個進程的父進程干掉。但是ps下發現該進程的父進程已經是init了。這個情況讓我很迷惑,因為通常來說一旦init進程接管了Z狀態的進程就會調用wait將其回收。而且這是避免僵屍進程的一種重要手段,怎麼會出現這種情況呢?
後來仔細回想了下進程終止時的過程,忽然找到問題所在了。我們知道任何進程結束最終都會調用內核函數do_exit。而這個函數有兩個重要的事要完成:
(1)(作為父進程)給自己的子進程(如果存在)找一個新的父進程;
(2)(作為子進程)通知自己的父進程為自己“收屍”;
第一個事情比較簡單,如果要退出的進程時多線程進程,則可以將子進程托付給自己的兄弟線程,如果沒有這樣的線程,則托付給init進程;
第二件事情對於單線程的進程比較簡單,但多線程的進程就會復雜些。因為只有線程組的主線程才有資格通知父進程,線程組的其他線程終止時,並不通知父進程,也沒必要保留資源進入僵屍狀態,直接調用release_task函數釋放所有的資源就好。
由於父進程只認子進程的主線程,所以在線程組中,如果主線程終止了,但是線程組還有其他線程,那麼就不會通知父進程來收屍,直到線程組中最後一個線程退出時。換句話說:在用戶層面,可以調用pthread_exit讓主線程先“死”,但在內核態中,主線程的task_struct一定要保住,哪怕變為僵屍。
所以當主線程退出但是其他線程還在運行,這時主線程就會變為Z狀態,縱使init接管了主線程。
l測試代碼
- #include
- #include
- #include
- #include
- void thread_fun(void)
- {
- printf("begin thread...\n");
- sleep(100);
- printf("end thread...\n");
- }
- int main(int argc, char *argv[])
- {
- pthread_t tid;
- int ret;
- daemon(0, 0);//調用deamon是為了讓init接管主線程
- printf("process begin...\n");
- ret=pthread_create(&tid,NULL,(void *)thread_fun,NULL);
- if(ret !=0 )
- {
- printf("pthread error\n");
- exit(1);
- }
- printf("main thread is end...\n");
- pthread_exit(0);
- printf("never exce\n");
- }
我們創建線程執行sleep,然後主線程退出,當我們使用ps查看主線程狀態時結果如下:
18099 1 Zsl a.out
可以看到,雖然init接管了主線程,但是主線程依然是Z狀態。要想讓這個Z進程消失,有兩個辦法,一個是殺死整個進程,也就是kill 18099,在一個就是ps -eLf查找其他線程,然後kill掉。