轉載--Defunct殭屍進程

做者:hanover網絡

連接:Defunct進程殭屍進程異步

在測試基於 DirectFB+Gstreamer 的視頻聯播系統的一個 Demo 的時候,其中大量使用 system 調用的語句,例如在 menu 代碼中的 system("./play") ,並且屢次執行,這種狀況下,在 ps -ef 列表中出現了大量的 defunct 進程,對程序的運行時有害的。按說system的源碼中應該已經包含了wait,但也不能排除開發板上這個版本的system中可能沒有wait,總之,開發板上在調用system後添加wait以後,defunct進程不復存在了。函數

下面談談 defunct 進程,中文翻譯叫殭屍進程。下文整理於網絡以及APUE一書。性能

 

1、什麼是殭屍進程測試

 

在UNIX 系統中,一個進程結束了,可是他的父進程沒有等待(調用wait / waitpid)他,那麼他將變成一個殭屍進程。當用ps命令觀察進程的執行狀態時,看到這些進程的狀態欄爲defunct。殭屍進程是一個早已死亡的進程,但在進程表(processs table)中仍佔了一個位置(slot)。spa

 

可是若是該進程的父進程已經先結束了,那麼該進程就不會變成殭屍進程。由於每一個進程結束的時候,系統都會掃描當前系統中所運行的全部進程,看看有沒有哪一個進程是剛剛結束的這個進程的子進程,若是是的話,就由Init進程來接管他,成爲他的父進程,從而保證每一個進程都會有一個父進程。而Init進程會自動wait其子進程,所以被Init接管的全部進程都不會變成殭屍進程。翻譯

 

2、UNIX下進程的運做方式設計

 

每一個Unix進程在進程表裏都有一個進入點(entry),核心進程執行該進程時使用到的一切信息都存儲在進入點。當用 ps 命令察看系統中的進程信息時,看到的就是進程表中的相關數據。當以fork()系統調用創建一個新的進程後,核心進程就會在進程表中給這個新進程分配一個進入點,而後將相關信息存儲在該進入點所對應的進程表內。這些信息中有一項是其父進程的識別碼。code

 

子進程的結束和父進程的運行是一個異步過程,即父進程永遠沒法預測子進程到底何時結束。那麼會不會由於父進程太忙來不及 wait 子進程,或者說不知道子進程何時結束,而丟失子進程結束時的狀態信息呢?視頻

 

不會。由於UNIX提供了一種機制能夠保證,只要父進程想知道子進程結束時的狀態信息,就能夠獲得。這種機制就是:當子進程走完了本身的生命週期後,它會執行exit()系統調用,內核釋放該進程全部的資源,包括打開的文件,佔用的內存等。可是仍然爲其保留必定的信息(包括進程號the process ID,退出碼exit code,退出狀態the terminationstatus of the process,運行時間the amount of CPU time taken by the process等),這些數據會一直保留到系統將它傳遞給它的父進程爲止,直到父進程經過wait / waitpid來取時才釋放。

 

也就是說,當一個進程死亡時,它並非徹底的消失了。進程終止,它再也不運行,可是還有一些殘留的數據等待父進程收回。當父進程 fork() 一個子進程後,它必須用 wait() (或者 waitpid())等待子進程退出。正是這個 wait() 動做來讓子進程的殘留數據消失。

 

3、殭屍進程的危害

 

若是父進程不調用wait / waitpid的話,那麼保留的那段信息就不會釋放,其進程號就會一直被佔用,可是系統的進程表容量是有限的,所能使用的進程號也是有限的,若是大量的產生殭屍進程,將由於沒有可用的進程號而致使系統不能產生新的進程。


因此,defunct進程不只佔用系統的內存資源,影響系統的性能,並且若是其數目太多,還會致使系統癱瘓。並且,因爲調度程序沒法選中Defunct 進程,因此不能用kill命令刪除Defunct 進程,唯一的方法只有重啓系統。

 

4、殭屍進程的產生

 

若是子進程死亡時父進程沒有 wait(),一般用 ps 能夠看到它被顯示爲「<defunct>」,這樣就產生了殭屍進程。它將永遠保持這樣直到父進程 wait()。

 
因而可知,defunct進程的出現時間是在子進程終止後,可是父進程還沒有讀取這些數據以前。利用這一點咱們能夠用下面的程序創建一個defunct 進程:
 
C代碼   收藏代碼
  1. #include <stdio.h>  
  2.   
  3. #include<sys/types.h>  
  4.   
  5. main()  
  6. {  
  7.   
  8.     if(!fork())  
  9.     {  
  10.   
  11.         printf(「child pid=%d\n」, getpid());  
  12.   
  13.         exit(0);  
  14.   
  15.     }  
  16.   
  17.     sleep(20);  
  18.   
  19.     printf(「parent pid=%d \n」, getpid());  
  20.   
  21.     exit(0);  
  22.   
  23. }  
 

當上述程序之後臺的方式執行時,第17行強迫程序睡眠20秒,讓用戶有時間輸入ps -e指令,觀察進程的狀態,咱們看到進程表中出現了defunct進程。當父進程執行終止後,再用ps -e命令觀察時,咱們會發現defunct進程也隨之消失。這是由於父進程終止後,init 進程會接管父進程留下的這些「孤兒進程」(orphan process),而這些「孤兒進程」執行完後,它在進程表中的進入點將被刪除。若是一個程序設計上有缺陷,就可能致使某個進程的父進程一直處於睡眠狀態或是陷入死循環,父進程沒有wait子進程,也沒有終止以使Init接管,該子進程執行結束後就變成了defunct進程,這個defunct 進程可能會一直留在系統中直到系統從新啓動。

 

 

在看一個產生殭屍進程的例子。

子進程要執行的程序test_prog

 

 

C代碼   收藏代碼
  1. //test.c  
  2. #include <stdio.h>  
  3. int main()  
  4. {  
  5.         int i = 0;  
  6.         for (i = 0 ; i < 10; i++)  
  7.         {  
  8.                 printf ("child time %d\n", i+1);  
  9.                 sleep (1);  
  10.         }  
  11.         return 0;  
  12. }  

 

 

 

父進程father的代碼father.c

 

 

C代碼   收藏代碼
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <sys/types.h>  
  4. #include <sys/wait.h>  
  5. int main()  
  6. {  
  7.         int pid = fork ();  
  8.         if (pid == 0)  
  9.         {  
  10.                 system ("./test_prog");  
  11.                 _exit (0);  
  12.         }else  
  13.         {  
  14.                 int i = 0;  
  15.                 /* 
  16.                                 int status = 0; 
  17.                 while (!waitpid(pid, &status, WNOHANG)) 
  18.                 { 
  19.                         printf ("father waiting%d\n", ++i); 
  20.                         sleep (1); 
  21.                 }*/  
  22.                 while (1)  
  23.                 {  
  24.                         printf ("father waiting over%d\n", ++i);  
  25.                         sleep (1);  
  26.                 }  
  27.                 return 0;  
  28.         }  
  29.   
  30. }  

 

 

 

執行./father,當子進程退出後,因爲父進程沒有對它的退出進行關注,會出現殭屍進程

 

 

C代碼   收藏代碼
  1. 20786 pts/0    00:00:00 father  
  2. 20787 pts/0    00:00:00 father <defunct>  

 

    總結:子進程成爲 defunct 直到父進程 wait(),除非父進程忽略了 SIGCLD 。更進一步,父進程沒有 wait() 就消亡(仍假設父進程沒有忽略 SIGCLD )的子進程(活動的或者 defunct)成爲 init 的子進程,init 着手處理它們。

 

5、如何避免殭屍進程

 

一、父進程經過wait和waitpid等函數等待子進程結束,這會致使父進程掛起。

 

在上個例子中,若是咱們略做修改,在第8行sleep()系統調用前執行wait()或waitpid()系統調用,則子進程在終止後會當即把它在進程表中的數據返回給父進程,此時系統會當即刪除該進入點。在這種情形下就不會產生defunct進程。


2. 若是父進程很忙,那麼能夠用signal函數爲SIGCHLD安裝handler。在子進程結束後,父進程會收到該信號,能夠在handler中調用wait回收。

 

3. 若是父進程不關心子進程何時結束,那麼能夠用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知內核,本身對子進程的結束不感興趣,那麼子進程結束後,內核會回收,並再也不給父進程發送信號 


4. fork兩次,父進程fork一個子進程,而後繼續工做,子進程fork一個孫進程後退出,那麼孫進程被init接管,孫進程結束後,init會回收。不過子進程的回收還要本身作。 下面就是Stevens給的採用兩次folk避免殭屍進程的示例:

 

 

C代碼   收藏代碼
    1. #include "apue.h"  
    2. #include <sys/wait.h>  
    3.   
    4. int  
    5. main(void)  
    6. ...{  
    7.      pid_t    pid;  
    8.   
    9.     if ((pid = fork()) < 0) ...{  
    10.          err_sys("fork error");  
    11.      } else if (pid == 0) ...{     /**//* first child */  
    12.         if ((pid = fork()) < 0)  
    13.              err_sys("fork error");  
    14.         else if (pid > 0)  
    15.              exit(0);    /**//* parent from second fork == first child */  
    16.         /**//* 
    17.           * We're the second child; our parent becomes init as soon 
    18.           * as our real parent calls exit() in the statement above. 
    19.           * Here's where we'd continue executing, knowing that when 
    20.           * we're done, init will reap our status. 
    21.          */  
    22.          sleep(2);  
    23.          printf("second child, parent pid = %d ", getppid());  
    24.          exit(0);  
    25.      }  
    26.       
    27.     if (waitpid(pid, NULL, 0) != pid)  /**//* wait for first child */  
    28.          err_sys("waitpid error");  
    29.   
    30.     /**//* 
    31.       * We're the parent (the original process); we continue executing, 
    32.       * knowing that we're not the parent of the second child. 
    33.      */  
    34.      exit(0);  
    35. }  
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息