什么是僵尸進(jìn)程?
首先內核會(huì )釋放終止進(jìn)程(調用了exit系統調用)所使用的所有存儲區,關(guān)閉所有打開(kāi)的文件等,但內核為每一個(gè)終止子進(jìn)程保存了一定量的信息。這些信息至少包括進(jìn)程ID,進(jìn)程的終止狀態(tài),以及該進(jìn)程使用的CPU時(shí)間,所以當終止子進(jìn)程的父進(jìn)程調用wait或waitpid時(shí)就可以得到這些信息。
而僵尸進(jìn)程就是指:一個(gè)進(jìn)程執行了exit系統調用退出,而其父進(jìn)程并沒(méi)有為它收尸(調用wait或waitpid來(lái)獲得它的結束狀態(tài))的進(jìn)程。
任何一個(gè)子進(jìn)程(init除外)在exit后并非馬上就消失,而是留下一個(gè)稱(chēng)外僵尸進(jìn)程的數據結構,等待父進(jìn)程處理。這是每個(gè)子進(jìn)程都必需經(jīng)歷的階段。另外子進(jìn)程退出的時(shí)候會(huì )向其父進(jìn)程發(fā)送一個(gè)SIGCHLD信號。
如何避免僵尸進(jìn)程
通過(guò)signal(SIGCHLD, SIG_IGN)通知內核對子進(jìn)程的結束不關(guān)心,由內核回收
父進(jìn)程調用wait/waitpid等函數等待子進(jìn)程結束,如果尚無(wú)子進(jìn)程退出wait會(huì )導致父進(jìn)程阻塞。waitpid可以通過(guò)傳遞WNOHANG使父進(jìn)程不阻塞立即返回。
如果父進(jìn)程很忙可以用signal注冊信號處理函數,在信號處理函數調用wait/waitpid等待子進(jìn)程退出。
通過(guò)兩次調用fork。父進(jìn)程首先調用fork創(chuàng )建一個(gè)子進(jìn)程然后waitpid等待子進(jìn)程退出,子進(jìn)程再fork一個(gè)孫進(jìn)程后退出。這樣子進(jìn)程退出后會(huì )被父進(jìn)程等待回收,而對于孫子進(jìn)程其父進(jìn)程已經(jīng)退出所以孫進(jìn)程成為一個(gè)孤兒進(jìn)程,孤兒進(jìn)程由init進(jìn)程接管,孫進(jìn)程結束后,init會(huì )等待回收。
如以下代碼會(huì )創(chuàng )建100個(gè)子進(jìn)程,但是父進(jìn)程并未等待它們結束,所以在父進(jìn)程退出前會(huì )有100個(gè)僵尸進(jìn)程。
C
#include <stdio.h>
#include <unistd.h>
int main() {
int i;
pid_t pid;
for(i=0; i<100; i++) {
pid = fork();
if(pid == 0)
break;
}
if(pid>0) {
printf("press Enter to exit...");
getchar();
}
return 0;
}
其中一個(gè)解決方法即是編寫(xiě)一個(gè)SIGCHLD信號處理程序來(lái)調用wait/waitpid來(lái)等待子進(jìn)程返回。
C
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
void wait4children(int signo) {
int status;
wait(&status);
}
int main() {
int i;
pid_t pid;
signal(SIGCHLD, wait4children);
for(i=0; i<100; i++) {
pid = fork();
if(pid == 0)
break;
}
if(pid>0) {
printf("press Enter to exit...");
getchar();
}
return 0;
}
但是通過(guò)運行程序發(fā)現還是會(huì )有僵尸進(jìn)程,而且每次僵尸進(jìn)程的數量都不定。這是為什么呢?其實(shí)主要是因為L(cháng)inux的信號機制是不排隊的,假如在某一時(shí)間段多個(gè)子進(jìn)程退出后都會(huì )發(fā)出SIGCHLD信號,但父進(jìn)程來(lái)不及一個(gè)一個(gè)地響應,所以最后父進(jìn)程實(shí)際上只執行了一次信號處理函數。但執行一次信號處理函數只等待一個(gè)子進(jìn)程退出,所以最后會(huì )有一些子進(jìn)程依然是僵尸進(jìn)程。
雖然這樣但是有一點(diǎn)是明了的,就是收到SIGCHLD必然有子進(jìn)程退出,而我們可以在信號處理函數里循環(huán)調用waitpid函數來(lái)等待所有的退出的子進(jìn)程。至于為什么不用wait,主要原因是在wait在清理完所有僵尸進(jìn)程后再次等待會(huì )阻塞。
所以最佳方案(個(gè)人觀(guān)點(diǎn))如下:
C
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
void wait4children(int signo) {
int status;
while(waitpid(-1, &status, WNOHANG) > 0);
}
int main() {
int i;
pid_t pid;
signal(SIGCHLD, wait4children);
for(i=0; i<100; i++) {
pid = fork();
if(pid == 0)
break;
}
if(pid>0) {
printf("press Enter to exit...");
getchar();
}
return 0;
}
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請
點(diǎn)擊舉報。