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