異步回收fork出的子進程(殭屍進程)

 什麼是殭屍進程

  一個進程在調用exit命令結束自己的生命的時候,其實它並沒有真正的被  殭屍進程銷燬, 而是留下一個稱爲殭屍進程(Zombie)的數據結構(系統調用exit,它的作用是 使進程退出,但也僅僅限於將一個正常的進程變成一個殭屍進程,並不能將其完全銷燬)

殭屍進程是怎麼樣產生

  在Linux進程的狀態中,殭屍進程是非常特殊的一種,它已經放棄了幾乎所有內存空間,沒有任何可執行代碼,也不能被調度,僅僅在進程列表中保留一個位置,記載該進程的退出狀態等信息供其他進程收集,除此之外,殭屍進程不再佔有任何內存空間。它需要它的父進程來爲它收屍。

  如果他的父進程沒安裝SIGCHLD信號處理函數調用wait或waitpid()等待子進程結束,又沒有顯式忽略該信號,那麼它就一直保持殭屍狀態,如果這時父進程結束了,那麼init進程自動會接手這個子進程,爲它收屍,它還是能被清除的。

  但是如果父進程是一個循環,不會結束,那麼子進程就會一直保持殭屍狀態,這就是爲什麼系統中有時會有很多的殭屍進程。系統所能使用的進程號是有限的,如果大量的產生僵死進程,將因爲沒有可用的進程號而導致系統不能產生新的進程.

 

異步回收殭屍進程:

fork()之後,非阻塞(異步)等待子進程(回收殭屍)。
fork()之後,子進程和父進程分叉執行,殭屍進程的產生是因爲父進程沒有給子進程“收屍”造成的,又可以根據危害程度分爲下述兩類:
總體來說:當子進程結束之後,但父進程未結束之前,子進程將成爲殭屍進程。
(1)當子進程結束之後,但父進程未結束之前,子進程將成爲殭屍進程,父進程結束後殭屍被init進程回收。
(2)如果子進程結束了,但是父進程始終沒有結束,那麼這個殭屍將一直存在,而且隨着exec,殭屍越來越多。
如下面的代碼,在父進程執行的5s內,子進程將爲殭屍:

  1.     
  2. #include <stdio.h>   
  3. #include <stdlib.h>   
  4. #include <signal.h>   
  5. #include <unistd.h>   
  6.     
  7. int main() {   
  8.     //子進程的pid   
  9.     int c_pid;   
  10.     int pid;   
  11.     
  12.     if ((pid = fork())) {   
  13.         //父進程   
  14.         c_pid = pid;   
  15.         printf("The child process is %d\n", c_pid);   
  16.         sleep(5);   
  17.         exit(0);   
  18.     } else {   
  19.         //子進程   
  20.         printf("I 'm a child.\n");   
  21.         exit(0);   
  22.     }   
  23. }  

如上面的代碼,在父進程的5s內,子進程一直是殭屍!
因此,需要對殭屍進程進行回收,傳統的回收方法是,使用wait()函數,等待子進程,wait()是阻塞模式的,當子進程沒有結束之前,wait一直等待,不往下面的語句執行。  

  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <signal.h>   
  4. #include <unistd.h>   
  5. #include <sys/wait.h>   
  6.     
  7. int main() {   
  8.     //子進程的pid   
  9.     int c_pid;   
  10.     int pid;   
  11.     
  12.     if ((pid = fork())) {   
  13.         //父進程   
  14.         c_pid = pid;   
  15.         printf("The child process is %d\n", c_pid);   
  16.         //阻塞等待子進程   
  17.         int status;   
  18.         if ((pid = wait(&status)) != -1 && pid == c_pid) {   
  19.             //成功回收子進程   
  20.             printf("The child exit with %d\n", WEXITSTATUS(status));   
  21.             fflush(stdin);   
  22.         } else {   
  23.             printf("wait() fail.\n");   
  24.         }   
  25.         printf("Now , The child has been exit , and I will sleep.\n");   
  26.         sleep(20);   
  27.         exit(0);   
  28.     } else {   
  29.         //子進程   
  30.         printf("I 'm a child.\n");   
  31.         sleep(5);   
  32.         exit(0);   
  33.     }   
  34. }  

如上面的代碼,在子進程執行5秒後,即被回收,在夫進程的20秒內,子進程已經被結束,不再是殭屍。
但是這種利用wait()阻塞等待的方法也有一定的缺陷,那就是父進程必須等待子進程,無法做其他事情,如何非阻塞的等待子進程呢?
man wait,查看NOTES章節,可以找到:
子進程退出的時候,會發送SIGCHLD信號,默認的POSIX不響應,所以,我們只需要把處理SIGCHLD的函數自己實現就OK了,怎麼作呢?
signal用於設置處理信號量的規則(或跳轉到的函數)

  1. signal(SIGCHLD,handler);   
  2. void handler(int num)   
  3. {   
  4.     //我接受到了SIGCHLD的信號啦   
  5.     int status;   
  6.     int pid = waitpid(-1,&status,WNOHANG);   
  7.     if(WIFEXITED(status))   
  8.     {   
  9.         printf("The child exit with code %d",WEXITSTATUS(status));   
  10.     }   
  11. }  

OK,全部代碼如下,注意父進程不要再用wait阻塞啦!

  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <signal.h>   
  4. #include <unistd.h>   
  5. #include <sys/wait.h>   
  6.     
  7. void handler(int num) {   
  8.     //我接受到了SIGCHLD的信號啦   
  9.     int status;   
  10.     int pid = waitpid(-1, &status, WNOHANG);   
  11.     if (WIFEXITED(status)) {   
  12.         printf("The child %d exit with code %d\n", pid, WEXITSTATUS(status));   
  13.     }   
  14. }   
  15.     
  16. int main() {   
  17.     //子進程的pid   
  18.     int c_pid;   
  19.     int pid;   
  20.     
  21.     signal(SIGCHLD, handler);   
  22.     
  23.     if ((pid = fork())) {   
  24.         //父進程   
  25.         c_pid = pid;   
  26.         printf("The child process is %d\n", c_pid);   
  27.     
  28.         //父進程不用等待,做自己的事情吧~   
  29.         for (int i = 0; i < 10; i++) {   
  30.             printf("Do parent things.\n");   
  31.             sleep(1);   
  32.         }   
  33.     
  34.         exit(0);   
  35.     } else {   
  36.         //子進程   
  37.         printf("I 'm a child.\n");   
  38.         sleep(2);   
  39.         exit(0);   
  40.     }   
  41. }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章