淺議Unix進程中defunct
我們在Unix系統(tǒng)管理中,當用ps命令觀察Unix進程的執(zhí)行狀態(tài)時,經(jīng)常看到某些Unix進程的狀態(tài)欄為defunct,這就是所謂的“僵尸”進程。“僵尸”進程是一個早已死亡的進程,但在進程表(processs table)中仍占了一個位置(slot)。由于Unix進程表的容量是有限的,所以,defunct進程不僅占用系統(tǒng)的內(nèi)存資源,影響系統(tǒng)的性能,而且如果其數(shù)目太多,還會導致系統(tǒng)癱瘓。
我們知道,每個Unix進程在進程表里都有一個進入點(entry),核心程序執(zhí)行該Unix進程時使用到的一切信息都存儲在進入點。當用ps命令察看系統(tǒng)中的進程信息時,看到的就是進程表中的相關數(shù)據(jù)。
當以fork()系統(tǒng)調(diào)用建立一個新的進程后,核心進程就會在進程表中給這個新進程分配一個進入點,然后將相關信息存儲在該進入點所對應的進程表內(nèi)。這些信息中有一項是其父進程的識別碼。
當這個進程走完了自己的生命周期后,它會執(zhí)行exit()系統(tǒng)調(diào)用,此時原來Unix進程表中的數(shù)據(jù)會被該進程的退出碼(exit code)、執(zhí)行時所用的CPU時間等數(shù)據(jù)所取代,這些數(shù)據(jù)會一直保留到系統(tǒng)將它傳遞給它的父進程為止。由此可見,defunct進程的出現(xiàn)時間是在子進程終止后,但是父進程尚未讀取這些數(shù)據(jù)之前。利用這一點我們可以用下面的程序建立一個defunct 進程:
- CODE:#include
- #include
- main(){
- if(!fork()){
- printf(“child pid=%d\n”, getpid());
- exit(0)
- }
- sleep(20);
- printf(“parent pid=%d \n”, getpid());
- exit(0);
- }
當上述程序以后臺的方式執(zhí)行時,第8行強迫程序睡眠20秒,讓用戶有時間輸入ps -e指令,觀察進程的狀態(tài)。當父進程執(zhí)行終止后,再用ps -e命令觀察時,我們會發(fā)現(xiàn)defunct進程也隨之消失。這是因為父進程終止后,init 進程會接管父進程留下的這些“孤兒進程”(orphan process),而這些“孤兒進程”執(zhí)行完后,它在進程表中的進入點將被刪除。
如果一個程序設計上有缺陷,就可能導致某個Unix進程的父進程一直處于睡眠狀態(tài)或是陷入死循環(huán),那么當該子進程執(zhí)行結束后就變成了defunct進程,這個defunct 進程可能會一直留在系統(tǒng)中直到系統(tǒng)重新啟動。
如果我們將上述程序略作修改,在第8行sleep()系統(tǒng)調(diào)用前執(zhí)行wait()或waitpid()系統(tǒng)調(diào)用,則子進程在終止后會立即把它在Unix進程表中的數(shù)據(jù)返回給父進程,此時系統(tǒng)會立即刪除該進入點。在這種情形下就不會產(chǎn)生defunct進程。
由于調(diào)度程序無法選中Defunct 進程,所以不能用kill命令刪除Defunct 進程,惟一的方法只有重啟系統(tǒng)。
【編輯推薦】