維護的第一個商業服務就用了兩次fork產生守護進程的做法,前兩天在網上看到許多帖子以及一些unix書籍,認為一次fork後產生守護進程足夠了,各有道理吧,不過多了一次fork到底是出於什麼目的呢?
進程也就是task,看看內核裡維護進程的數據結構task_struct,這裡有兩個成員:
[cpp]
- struct task_struct {
- volatile long state;
- int exit_state;
- ...
- }
看看include/linux/sched.h裡的value取值:
[cpp]
- #define TASK_RUNNING 0
- #define TASK_INTERRUPTIBLE 1
- #define TASK_UNINTERRUPTIBLE 2
- #define __TASK_STOPPED 4
- #define __TASK_TRACED 8
- /* in tsk->exit_state */
- #define EXIT_ZOMBIE 16
- #define EXIT_DEAD 32
- /* in tsk->state again */
- #define TASK_DEAD 64
- #define TASK_WAKEKILL 128
- #define TASK_WAKING 256
- #define TASK_STATE_MAX 512
可以看到,進程狀態裡除了大家都理解的running/interuptible/uninterruptible/stop等狀態外,還有一個ZOMBIE狀態,這個狀態是怎麼回事呢?
這是因為linux裡的進程都屬於一顆樹,樹的根結點是linux系統初始化結束階段時啟動的init進程,這個進程的pid是1,所有的其他進程都是它的子孫。除了init,任何進程一定有他的父進程,而父進程會負責分配(fork)、回收(wait4)它申請的進程資源。這個樹狀關系也比較健壯,當某個進程還在運行時,它的父進程卻退出了,這個進程卻沒有成為孤兒進程,因為linux有一個機制,init進程會接管它,成為它的父進程。這也是守護進程的由來了,因為守護進程的其中一個要求就是希望init成為守護進程的父進程。
如果某個進程自身終止了,在調用exit清理完相關的內容文件等資源後,它就會進入ZOMBIE狀態,它的父進程會調用wait4來回收這個task_struct,但是,如果父進程一直沒有調用wait4去釋放子進程的task_struct,問題就來了,這個task_struct誰來回收呢?永遠沒有人,除非父進程終止後,被init進程接管這個ZOMBIE進程,然後調用wait4來回收進程描述符。如果父進程一直在運行著,這個ZOMBIE會永遠的占用系統資源,用KILL發任何信號量也不能釋放它。這是很可怕的,因為服務器上可能會出現無數ZOMBIE進程導致機器掛掉。