僵死(Zombie)進(jìn)程
進(jìn)程在它的生命周期有幾種狀態(tài):睡眠,可運(yùn)行,停止,正在運(yùn)行和僵死狀態(tài)。所謂僵死進(jìn)程,指的是一個(gè)進(jìn)程已經(jīng)退出,它的內(nèi)存和相關(guān)的資源已經(jīng)被內(nèi)核釋放掉,但是在進(jìn)程表中這個(gè)進(jìn)程項(xiàng)(entry)還保留著,以便它的父進(jìn)程得到它的退出狀態(tài)。一個(gè)進(jìn)程退出時(shí),它的父進(jìn)程會(huì)收到一個(gè)SIGCHLD信號(hào)。一般情況下,這個(gè)信號(hào)的句柄通常執(zhí)行wait系統(tǒng)調(diào)用,這樣處于僵死狀態(tài)的進(jìn)程會(huì)被刪除。如果父進(jìn)程沒(méi)有這么做,結(jié)果是什么呢?毫無(wú)疑問(wèn),進(jìn)程會(huì)處于僵死狀態(tài)。實(shí)際上,僵死進(jìn)程不會(huì)對(duì)系統(tǒng)有太大的傷害,最多就是它的進(jìn)程號(hào)(PID)和進(jìn)程表中的進(jìn)程項(xiàng)系統(tǒng)不能使用。
ps aux|grep defunct
找出僵死進(jìn)程
注意用kill命令不能殺死這種進(jìn)程。原因是它已經(jīng)退出了,什么也沒(méi)有了,自然無(wú)法收到任何信號(hào)。
刪除僵尸進(jìn)程的根本方法是讓它的父進(jìn)程調(diào)用wait來(lái)處理SIGCHLD信號(hào)。有兩種方式可以做到這一點(diǎn)。一種方法是改變它的父進(jìn)程。可以用kill命令殺死它的父進(jìn)程,這樣init變成它的新的父進(jìn)程,而init會(huì)定時(shí)地執(zhí)行wait系統(tǒng)調(diào)用。另一種方式是使用調(diào)試器,在父進(jìn)程中執(zhí)行wait系統(tǒng)調(diào)用。如果知道了父進(jìn)程的程序名稱(chēng)和它的進(jìn)程號(hào),運(yùn)行下面命令:
%gdb `parent application name` `parent pid`
(gdb) set unwindonsignal on
(gdb) call wait(pid of zombie process)
gdb會(huì)在父進(jìn)程中調(diào)用wait,從而達(dá)到我們的目的。注意,unwindonsignal要被set為on, 它告訴gdb把堆棧恢復(fù)到調(diào)用wait之前的狀態(tài)。要不然父進(jìn)程會(huì)crash。 在程序中避免僵死進(jìn)程除了顯式調(diào)用wait或waitpid外,也可以使用下面的代碼來(lái)避免僵死進(jìn)程(這兒假設(shè)父進(jìn)程對(duì)子進(jìn)程的狀態(tài)不感興趣),它遵循POSIX,是可移植的。
struct sigaction sa;
sa.sa_handler = SIG_IGN;
sa.sa_flags = SA_NOCLDWAIT;
sigemptyset (&sa.sa_mask);
sigaction (SIGCHLD, &sa, NULL);
參考exit(2)手冊(cè)頁(yè)。