wait WNOHANG 殭屍進程

什麼是殭屍進程?
首先內核會釋放終止進程(調用了exit系統調用)所使用的所有存儲區,關閉所有打開的文件等,但內核爲每一個終止子進程保存了一定量的信息。這些信息至少包括進程ID,進程的終止狀態,以及該進程使用的CPU時間,所以當終止子進程的父進程調用wait或waitpid時就可以得到這些信息。
而殭屍進程就是指:一個進程執行了exit系統調用退出,而其父進程並沒有爲它收屍(調用wait或waitpid來獲得它的結束狀態)的進程。
任何一個子進程(init除外)在exit後並非馬上就消失,而是留下一個稱外殭屍進程的數據結構,等待父進程處理。這是每個子進程都必需經歷的階段。另外子進程退出的時候會向其父進程發送一個SIGCHLD信號。
如何避免殭屍進程
通過signal(SIGCHLD, SIG_IGN)通知內核對子進程的結束不關心,由內核回收
父進程調用wait/waitpid等函數等待子進程結束,如果尚無子進程退出wait會導致父進程阻塞。waitpid可以通過傳遞WNOHANG使父進程不阻塞立即返回。
如果父進程很忙可以用signal註冊信號處理函數,在信號處理函數調用wait/waitpid等待子進程退出。
通過兩次調用fork。父進程首先調用fork創建一個子進程然後waitpid等待子進程退出,子進程再fork一個孫進程後退出。這樣子進程退出後會被父進程等待回收,而對於孫子進程其父進程已經退出所以孫進程成爲一個孤兒進程,孤兒進程由init進程接管,孫進程結束後,init會等待回收。
如以下代碼會創建100個子進程,但是父進程並未等待它們結束,所以在父進程退出前會有100個殭屍進程。
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;
}
其中一個解決方法即是編寫一個SIGCHLD信號處理程序來調用wait/waitpid來等待子進程返回。
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;
}
但是通過運行程序發現還是會有殭屍進程,而且每次殭屍進程的數量都不定。這是爲什麼呢?其實主要是因爲Linux的信號機制是不排隊的,假如在某一時間段多個子進程退出後都會發出SIGCHLD信號,但父進程來不及一個一個地響應,所以最後父進程實際上只執行了一次信號處理函數。但執行一次信號處理函數只等待一個子進程退出,所以最後會有一些子進程依然是殭屍進程。
雖然這樣但是有一點是明瞭的,就是收到SIGCHLD必然有子進程退出,而我們可以在信號處理函數裏循環調用waitpid函數來等待所有的退出的子進程。至於爲什麼不用wait,主要原因是在wait在清理完所有殭屍進程後再次等待會阻塞。
所以最佳方案(個人觀點)如下:
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章