僵屍進程的產生原因和避免方法



分類: linux 2013-01-14 22:11  1867人閱讀  評論(0)  收藏  舉報

僵屍進程的產生:

當一個進程創建了一個子進程時,他們的運行時異步的。即父進程無法預知子進程會在什么時候結束,那么如果父進程很繁忙來不及wait 子進程時,那么當子進程結束時,會不會丟失子進程的結束時的狀態信息呢?處於這種考慮unix提供了一種機制可以保證只要父進程想知道子進程結束時的信息,它就可以得到。

這種機制是:在每個進程退出的時候,內核釋放該進程所有的資源,包括打開的文件,占用的內存。但是仍然保留了一些信息(如進程號pid 退出狀態 運行時間等)。這些保留的信息直到進程通過調用wait/waitpid時才會釋放。這樣就導致了一個問題,如果沒有調用wait/waitpid的話,那么保留的信息就不會釋放。比如進程號就會被一直占用了。但系統所能使用的進程號的有限的,如果產生大量的僵屍進程,將導致系統沒有可用的進程號而導致系統不能創建進程。所以我們應該避免僵屍進程

 

這里有一個需要注意的地方。如果子進程先結束而父進程后結束,即子進程結束后,父進程還在繼續運行但是並未調用wait/waitpid那子進程就會成為僵屍進程。

 

但如果子進程后結束,即父進程先結束了,但沒有調用wait/waitpid來等待子進程的結束,此時子進程還在運行,父進程已經結束。那么並不會產生僵屍進程。應為每個進程結束時,系統都會掃描當前系統中運行的所有進程,看看有沒有哪個進程時剛剛結束的這個進程的子進程,如果有,就有init來接管它,成為它的父進程。

 

同樣的在產生僵屍進程的那種情況下,即子進程結束了但父進程還在繼續運行(並未調用wait/waitpid)這段期間,假如父進程異常終止了,那么該子進程就會自動被init接管。那么它就不再是僵屍進程了。應為intit會發現並釋放它所占有的資源。(當然如果進程表越大,init發現它接管僵屍進程這個過程就會變得越慢,所以在init為發現他們之前,僵屍進程依舊消耗着系統的資源)

 

 

我們先來討論 父進程先結束的情況:

 

比如這段代碼。我們讓子進程循環打印5次語句 父進程循環打印3次語句。並在父進程中調用wait()等待子進程的結束//

  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<unistd.h>  
  4. #include<sys/types.h>  
  5. #include<sys/wait.h>  
  6. int main()  
  7. {  
  8.        int count;  
  9.        pid_t pid;  
  10.        char *message;   
  11.        printf("fork program starting\n");  
  12.    
  13.        pid=fork();  
  14.        switch(pid)  
  15.        {    
  16.                 case -1:perror("forkerror");  
  17.                         exit(EXIT_FAILURE);  
  18.                         break;  
  19.                 case 0 :message="This isthe children";  
  20.                         count=10;  
  21.                         break;  
  22.                 default:message="This isthe parent.";  
  23.                         count=3;  
  24.                         break;  
  25.         }    
  26.        for(;count>0;count--)  
  27.        {    
  28.                printf("%s\n",message);  
  29.                 sleep(1);  
  30.        }    
  31.        if(pid)  
  32.                 wait((int *)0);  
  33.        if(pid)  
  34.                 printf("Father programDone.\n");  
  35.        else  
  36.                 printf("Child ProgramDnoe\n");  
  37.        exit(0);  
  38.       
  39. }  


我們讓程序在后台運行,並用ps命令查看進程狀態。

 

 

我們從輸出中看到

第一行顯示了我們運行的進程pid是27324

Ps 的輸出中我們看到了他有一個2735的子進程,

父進程循環三次后並不會結束,而是等待子進程結束后再結束。

這里並未產生僵屍進程

 

 

如果我們不等帶子進程的結束

 if(pid)  

wait((int *)0)   注釋掉

將產生如下輸出

 

從第一行我們看到我們運行的程序pid為2804

Ps輸出中的pid為2805 是創建的子進程。我們是在父進程結束后(未調用wait,所以父進程先結束)再用ps命令查看的。所以2805的父進程變成了1 (init 的pid),因為2804已經結束了,所以2805這個子進程被 init接管,同樣這里並未產生僵屍進程

 

現在我們來分析子進程后結束的情況:

我們  給出下面這個程序

  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<unistd.h>  
  4. #include<sys/types.h>  
  5.    
  6. int main()  
  7. {  
  8.        int count;  
  9.        char *message;  
  10.        pid_t pid;  
  11.    
  12.        pid=fork();  
  13.        switch(pid)  
  14.        {    
  15.                 case -1:  
  16.                         perror("forkerror");  
  17.                         exit(EXIT_FAILURE);  
  18.                 case 0:message="This isthe child.";  
  19.                         count=5;  
  20.                         break;  
  21.                 default:message="This isth parent.";  
  22.                         count=10;  
  23.                        break;  
  24.        }    
  25.        for(;count>0;count--)  
  26.        {    
  27.                printf("%s\n",message);  
  28.                 sleep(2);  
  29.        }    
  30.    
  31.        if(pid)  
  32.                 printf("Father programDone.\n");  
  33.        else  
  34.                 printf("Child prigramDone\n");  
  35.        exit(EXIT_SUCCESS);  
  36.    
  37. }  

這里的代碼改動很小,我們只是改變了父進程和子進程的 打印次數

並且在父進程中我們不調用wait/waitpid來釋放子進程的一些信息

 

 

 在子進程結束,但父進程還未結束時我們查看進程信息

第一行我們看到 我們運行的程序pid 是2874,它的子進程我們可以從ps輸出中看到為2875

 我們注意到pid為2875的進程這時候成了僵屍進程。如果主線程運行的時間足夠長,那么該僵屍進程就會一直存在着並占用着系統的一些資源。

 

我們已盡知道了僵屍進程的產生原因,那么如何避免僵屍進程呢

 

如果父進程並不是很繁忙我們就可以通過直接調用wait/waitpid來等待子進程的結束。當然這會導致父進程被掛起。比如第一種情況中(父進程循環了三次,子進程循環了五次,子進程先結束,父進程調用wait等待子進程)父進程循環結束后並不會結束,而是被掛起等待子進程的結束。

 

但是如果父進程很忙。我們不希望父進程一直被掛起直到子進程的結束

那么我們可以使用信號函數sigaction為SIGCHLD設置wait處理函數。這樣子進程結束后,父進程就會收到子進程結束的信號。並調用wait回收子進程的資源

這里給出一個例程

 

  1. #include<sys/wait.h>  
  2. #include<stdio.h>  
  3. #include<stdlib.h>  
  4. #include<unistd.h>  
  5. #include<sys/types.h>  
  6. #include<signal.h>  
  7. void fun_act(int sig)  
  8. {  
  9.        wait((int *)0);  
  10. }  
  11. int main()  
  12. {  
  13.        int count;  
  14.        char *message;  
  15.        pid_t pid;  
  16.    
  17.        struct sigaction act;  
  18.        act.sa_handler=fun_act;  
  19.        sigemptyset(&act.sa_mask);  
  20.        act.sa_flags=0;  
  21.    
  22.        pid=fork();  
  23.        switch(pid)  
  24.        {    
  25.                 case -1:  
  26.                         perror("forkerror");  
  27.                         exit(EXIT_FAILURE);  
  28.    
  29.                 case 0:message="This isthe child.";  
  30.                         count=5;  
  31.                         break;  
  32.                              
  33.                 default:message="This isth parent.";  
  34.                         count=10;  
  35.                         break;  
  36.        }  
  37. if(pid)  
  38.                if(sigaction(SIGCHLD,&act,0)==-1)  
  39.                 {  
  40.                         perror("Settingsignal failed.");  
  41.                         exit(1);  
  42.                 }  
  43.        for(;count>0;count--)  
  44.        {  
  45.                printf("%s\n",message);  
  46.                 sleep(1);  
  47.        }  
  48.        if(pid)  
  49.                 printf("Father programDone.\n");  
  50.        else  
  51.                 printf("Child prigramDone\n");  
  52.        exit(EXIT_SUCCESS);  
  53.    
  54. }  




我們在子進程結束前 用 ps 查看了一次結束后也查看了一次。

從輸出我們看到,pid為2949的子進程正常結束了,並未產生僵屍進程。說明子進程結束后,父進程收到了它結束的消息,並調用了wait回收了子進程的資源。從而避免了僵屍進程的產生。

 

轉至:http://blog.csdn.net/feng574912883/article/details/8502667 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM