linux下的僵尸进程处理SIGCHLD信号【转】
轉(zhuǎn)自:http://www.cnblogs.com/wuchanming/p/4020463.html
什么是僵尸進程?
首先內(nèi)核會釋放終止進程(調(diào)用了exit系統(tǒng)調(diào)用)所使用的所有存儲區(qū),關(guān)閉所有打開的文件等,但內(nèi)核為每一個終止子進程保存了一定量的信息。這些信息至少包括進程ID,進程的終止?fàn)顟B(tài),以及該進程使用的CPU時間,所以當(dāng)終止子進程的父進程調(diào)用wait或waitpid時就可以得到這些信息。
而僵尸進程就是指:一個進程執(zhí)行了exit系統(tǒng)調(diào)用退出,而其父進程并沒有為它收尸(調(diào)用wait或waitpid來獲得它的結(jié)束狀態(tài))的進程。
任何一個子進程(init除外)在exit后并非馬上就消失,而是留下一個稱外僵尸進程的數(shù)據(jù)結(jié)構(gòu),等待父進程處理。這是每個子進程都必需經(jīng)歷的階段。另外子進程退出的時候會向其父進程發(fā)送一個SIGCHLD信號。
?
僵尸進程的目的?
設(shè)置僵死狀態(tài)的目的是維護子進程的信息,以便父進程在以后某個時候獲取。這些信息至少包括進程ID,進程的終止?fàn)顟B(tài),以及該進程使用的CPU時間,所以當(dāng)終止子進程的父進程調(diào)用wait或waitpid時就可以得到這些信息。如果一個進程終止,而該進程有子進程處于僵尸狀態(tài),那么它的所有僵尸子進程的父進程ID將被重置為1(init進程)。繼承這些子進程的init進程將清理它們(也就是說init進程將wait它們,從而去除它們的僵尸狀態(tài))。
?
如何避免僵尸進程?
第一種方法忽略SIGCHLD信號,這常用于并發(fā)服務(wù)器的性能的一個技巧因為并發(fā)服務(wù)器常常fork很多子進程,子進程終結(jié)之后需要服務(wù)器進程去wait清理資源。如果將此信號的處理方式設(shè)為忽略,可讓內(nèi)核把僵尸子進程轉(zhuǎn)交給init進程去處理,省去了大量僵尸進程占用系統(tǒng)資源。
?
僵尸進程處理辦法
1?wait()函數(shù)
#include <sys/types.h>?
#include <sys/wait.h>
pid_t wait(int *status);
進程一旦調(diào)用了wait,就立即阻塞自己,由wait自動分析是否當(dāng)前進程的某個子進程已經(jīng)退出,如果讓它找到了這樣一個已經(jīng)變成僵尸的子進程,wait就會收集這個子進程的信息,并把它徹底銷毀后返回;如果沒有找到這樣一個子進程,wait就會一直阻塞在這里,直到有一個出現(xiàn)為止。?
參數(shù)status用來保存被收集進程退出時的一些狀態(tài),它是一個指向int類型的指針。但如果我們對這個子進程是如何死掉的毫不在意,只想把這個僵尸進程消滅掉,(事實上絕大多數(shù)情況下,我們都會這樣想),我們就可以設(shè)定這個參數(shù)為NULL,就象下面這樣:
? pid = wait(NULL);
如果成功,wait會返回被收集的子進程的進程ID,如果調(diào)用進程沒有子進程,調(diào)用就會失敗,此時wait返回-1,同時errno被置為ECHILD。
- wait系統(tǒng)調(diào)用會使父進程暫停執(zhí)行,直到它的一個子進程結(jié)束為止。
- 返回的是子進程的PID,它通常是結(jié)束的子進程
- 狀態(tài)信息允許父進程判定子進程的退出狀態(tài),即從子進程的main函數(shù)返回的值或子進程中exit語句的退出碼。
- 如果status不是一個空指針,狀態(tài)信息將被寫入它指向的位置
可以上述的一些宏判斷子進程的退出情況:
?
2 waitpid()函數(shù)
#include <sys/types.h>?
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
參數(shù):
status:如果不是空,會把狀態(tài)信息寫到它指向的位置,與wait一樣
options:允許改變waitpid的行為,最有用的一個選項是WNOHANG,它的作用是防止waitpid把調(diào)用者的執(zhí)行掛起
The value of options is an OR of zero or more? of? the? following? con-?
stants:
WNOHANG???? return immediately if no child has exited.
WUNTRACED?? also? return? if? a? child? has stopped (but not traced via?
??????????? ptrace(2)).? Status for traced children which have? stopped?
??????????? is provided even if this option is not specified.
WCONTINUED (since Linux 2.6.10)?
??????????? also return if a stopped child has been resumed by delivery?
??????????? of SIGCONT.
返回值:如果成功返回等待子進程的ID,失敗返回-1
對于waitpid的p i d參數(shù)的解釋與其值有關(guān):
pid == -1 等待任一子進程。于是在這一功能方面waitpid與wait等效。
pid > 0 等待其進程I D與p i d相等的子進程。
pid == 0 等待其組I D等于調(diào)用進程的組I D的任一子進程。換句話說是與調(diào)用者進程同在一個組的進程。
pid < -1 等待其組I D等于p i d的絕對值的任一子進程
wait與waitpid區(qū)別:
- 在一個子進程終止前, wait 使其調(diào)用者阻塞,而waitpid 有一選擇項,可使調(diào)用者不阻塞。
- waitpid并不等待第一個終止的子進程—它有若干個選擇項,可以控制它所等待的特定進程。
- 實際上wait函數(shù)是waitpid函數(shù)的一個特例。waitpid(-1, &status, 0);
?
示例:
如以下代碼會創(chuàng)建100個子進程,但是父進程并未等待它們結(jié)束,所以在父進程退出前會有100個僵尸進程。
#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; }其中一個解決方法即是編寫一個SIGCHLD信號處理程序來調(diào)用wait/waitpid來等待子進程返回。
?
#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; }但是通過運行程序發(fā)現(xiàn)還是會有僵尸進程,而且每次僵尸進程的數(shù)量都不定。這是為什么呢?其實主要是因為Linux的信號機制是不排隊的,假如在某一時間段多個子進程退出后都會發(fā)出SIGCHLD信號,但父進程來不及一個一個地響應(yīng),所以最后父進程實際上只執(zhí)行了一次信號處理函數(shù)。但執(zhí)行一次信號處理函數(shù)只等待一個子進程退出,所以最后會有一些子進程依然是僵尸進程。
雖然這樣但是有一點是明了的,就是收到SIGCHLD必然有子進程退出,而我們可以在信號處理函數(shù)里循環(huán)調(diào)用waitpid函數(shù)來等待所有的退出的子進程。至于為什么不用wait,主要原因是在wait在清理完所有僵尸進程后再次等待會阻塞。
?
所以最佳方案如下:
#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; }這里使用waitpid而不是使用wait的原因在于:我們在一個循環(huán)內(nèi)調(diào)用waitpid,以獲取所有已終止子進程的狀態(tài)。我們必須指定WNOHANG選項,它告訴waitpid在有尚未終止的子進程在運行時不要阻塞。我們不能在循環(huán)內(nèi)調(diào)用wait,因為沒有辦法防止wait在正運行的子進程尚有未終止時阻塞。
轉(zhuǎn)載于:https://www.cnblogs.com/sky-heaven/p/8074273.html
總結(jié)
以上是生活随笔為你收集整理的linux下的僵尸进程处理SIGCHLD信号【转】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Vim-复制选中内容至系统剪贴板,光标移
- 下一篇: [Android]Space控件的应用场