程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 僵屍進程的產生原因和避免方法

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

編輯:關於PHP編程

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


僵屍進程的產生:

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

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


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


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


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



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


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


#include#include#include#include#includeint main(){       int count;       pid_t pid;       char *message;        printf("fork program starting\n");        pid=fork();       switch(pid)       {                  case -1:perror("forkerror");                        exit(EXIT_FAILURE);                        break;                case 0 :message="This isthe children";                        count=10;                        break;                default:message="This isthe parent.";                        count=3;                        break;        }         for(;count>0;count--)       {                 printf("%s\n",message);                sleep(1);       }         if(pid)                wait((int *)0);       if(pid)                printf("Father programDone.\n");       else                printf("Child ProgramDnoe\n");       exit(0);    }



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


我們從輸出中看到

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

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

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

這裡並未產生僵屍進程



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

將if(pid)

wait((int *)0) 注釋掉

將產生如下輸出

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

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


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

我們 給出下面這個程序


#include#include#include#include int main(){       int count;       char *message;       pid_t pid;        pid=fork();       switch(pid)       {                  case -1:                        perror("forkerror");                        exit(EXIT_FAILURE);                case 0:message="This isthe child.";                        count=5;                        break;                default:message="This isth parent.";                        count=10;                       break;       }         for(;count>0;count--)       {                 printf("%s\n",message);                sleep(2);       }          if(pid)                printf("Father programDone.\n");       else                printf("Child prigramDone\n");       exit(EXIT_SUCCESS); }

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


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


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

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

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


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


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


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

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

這裡給出一個例程



#include#include#include#include#include#includevoid fun_act(int sig){       wait((int *)0);}int main(){       int count;       char *message;       pid_t pid;        struct sigaction act;       act.sa_handler=fun_act;       sigemptyset(&act.sa_mask);       act.sa_flags=0;        pid=fork();       switch(pid)       {                  case -1:                        perror("forkerror");                        exit(EXIT_FAILURE);                 case 0:message="This isthe child.";                        count=5;                        break;                                           default:message="This isth parent.";                        count=10;                        break;       }if(pid)               if(sigaction(SIGCHLD,&act,0)==-1)                {                        perror("Settingsignal failed.");                        exit(1);                }       for(;count>0;count--)       {               printf("%s\n",message);                sleep(1);       }       if(pid)                printf("Father programDone.\n");       else                printf("Child prigramDone\n");       exit(EXIT_SUCCESS); }




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

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved