什麼是殭屍進程? 服務器
首先內核會釋放終止進程(調用了exit系統調用)所使用的全部存儲區,關閉全部打開的文件等,但內核爲每個終止子進程保存了必定量的信息。這些信息至少包括進程ID,進程的終止狀態,以及該進程使用的CPU時間,因此當終止子進程的父進程調用wait或waitpid時就能夠獲得這些信息。 數據結構
而殭屍進程就是指:一個進程執行了exit系統調用退出,而其父進程並無爲它收屍(調用wait或waitpid來得到它的結束狀態)的進程。 併發
任何一個子進程(init除外)在exit後並不是立刻就消失,而是留下一個稱外殭屍進程的數據結構,等待父進程處理。這是每一個子進程都必需經歷的階段。另外子進程退出的時候會向其父進程發送一個SIGCHLD信號。
app
如何避免殭屍進程 函數
- 經過signal(SIGCHLD, SIG_IGN)通知內核對子進程的結束不關心,由內核回收
- 父進程調用wait/waitpid等函數等待子進程結束,若是尚無子進程退出wait會致使父進程阻塞。waitpid能夠經過傳遞WNOHANG使父進程不阻塞當即返回。
- 若是父進程很忙能夠用signal註冊信號處理函數,在信號處理函數調用wait/waitpid等待子進程退出。
- 經過兩次調用fork。父進程首先調用fork建立一個子進程而後waitpid等待子進程退出,子進程再fork一個孫進程後退出。這樣子進程退出後會被父進程等待回收,而對於孫子進程其父進程已經退出因此孫進程成爲一個孤兒進程,孤兒進程由init進程接管,孫進程結束後,init會等待回收。
第一種方法忽略SIGCHLD信號,這經常使用於併發服務器的性能的一個技巧由於併發服務器經常fork不少子進程,子進程終結以後須要服務器進程去wait清理資源。若是將此信號的處理方式設爲忽略,可以讓內核把殭屍子進程轉交給init進程去處理,省去了大量殭屍進程佔用系統資源
如如下代碼會建立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來等待子進程返回。 spa
- #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在清理完全部殭屍進程後再次等待會阻塞。
因此最佳方案(我的觀點)以下: .net
- #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;
- }