僵屍進程與孤兒進程
這部分參考了: https://www.cnblogs.com/Anker/p/3271773.html
linux提供一種機制使子進程在退出時候,父進程能夠收集到子進程的結束狀態信息(子進程pid,退出狀態,運行時間等)。父進程需要調用 wait/waitpid來獲取這些信息。父進程收集這些信息后這些信息才會釋放。
linux下新進程的創建可以由fork來產生新的子進程。然后根據fork的返回值(小於0,等於0,大於0)判斷是fork出錯,子進程還是父進程。通常情況下,父進程需要在子進程任務結束退出后做“善后”,也就是一些資源清理工作。子進程退出時會把打開的文件句柄,內存占用,打開的資源進行釋放,但是不會清理進程控制塊PCB信息。
孤兒進程:
父進程早於子進程退出時候子進程還在運行,子進程會成為孤兒進程。linux會對孤兒進程的處理,把孤兒進程的父進程設為1,也就是由init進程來托管。init進程負責子進程退出后的善后清理工作。
僵屍進程:
子進程退出后留下的進程信息沒有被收集,會導致占用的進程控制塊PCB不被釋放,形成僵屍進程。進程已經死去,但是進程資源沒有被釋放掉。
問題及危害
如果系統中存在大量的僵屍進程,他們的進程號就會一直被占用,但是系統所能使用的進程號是有限的,系統將因為沒有可用的進程號而導致系統不能產生新的進程.。
孤兒進程的資源收集由init進程負責,當一個孤兒進程凄涼地結束了其生命周期的時候,init進程就會處理它的一切善后工作。因此孤兒進程並不會有什么危害。
任何一個子進程(init除外)在exit()之后,並非馬上就消失掉,而是留下一個稱為僵屍進程(Zombie)的數據結構,等待父進程處理。這是每個 子進程在結束時都要經過的階段。如果子進程在exit()之后,父進程沒有來得及處理,這時用ps命令就能看到子進程的狀態是“Z”。如果父進程能及時 處理,可能用ps命令就來不及看到子進程的僵屍狀態,但這並不等於子進程不經過僵屍狀態。 如果父進程在子進程結束之前退出,則子進程將由init接管。init將會以父進程的身份對僵屍狀程的身份對僵屍狀態的子進程進行處理。
怎么避免僵屍進程
- 通過signal(SIGCHLD, SIG_IGN)通知內核對子進程的結束不關心,由內核回收。如果不想讓父進程掛起,可以在父進程中加入一條語句:signal(SIGCHLD,SIG_IGN);表示父進程忽略SIGCHLD信號,該信號是子進程退出的時候向父進程發送的。
- 父進程調用wait/waitpid等函數等待子進程結束,如果尚無子進程退出wait會導致父進程阻塞。waitpid可以通過傳遞WNOHANG使父進程不阻塞立即返回。
- 如果父進程很忙可以用signal注冊信號處理函數,在信號處理函數調用wait/waitpid等待子進程退出。
- 通過兩次調用fork。父進程首先調用fork創建一個子進程然后waitpid等待子進程退出,子進程再fork一個孫進程后退出。這樣子進程退出后會被父進程等待回收,而對於孫子進程其父進程已經退出所以孫進程成為一個孤兒進程,孤兒進程由init進程接管,孫進程結束后,init會等待回收。
- 殺死父進程。 如果僵屍進程的父進程還存在,找到這個父進程,kill掉它。這樣就會變成2的情況,init會負責善后工作。