linux僵死程式

weixin_34262482發表於2012-09-01

http://blog.chinaunix.net/uid-23089249-id-210808.html

 

一個程式在呼叫exit命令結束自己的生命的時候,其實它並沒有真正的被銷燬, 而是留下一個稱為僵死程式(Zombie)的資料結構(系統呼叫exit,它的作用是使程式退出,但也僅僅限於將一個正常的程式變成一個僵死程式,並不能將其完全銷燬)。

 

一、僵死程式的產生
    在每個程式退出的時候,核心釋放該程式所有的資源,包括開啟的檔案,佔用的記憶體等,但是仍然為其保留一定的資訊(包括程式號the process ID,退出狀態the 
termination status of the process,執行時間the amount of CPU time taken by the process等), 直到父程式通過wait/waitpid來取時才釋放。此時該程式處於僵死狀態,該程式成為僵死程式(Zombie Process)。 這保證了父程式可以獲取到子程式結束時的狀態資訊。
    在Linux程式的狀態中,僵死程式是非常特殊的一種,它已經放棄了幾乎所有記憶體空間,沒有任何可執行程式碼,也不能被排程,僅僅在程式列表中保留一個位置,記載該程式的退出狀態等資訊供其他程式收集,除此之外,僵死程式不再佔有任何記憶體空間。它需要它的父程式來為它收屍,如果他的父程式沒安裝SIGCHLD訊號處理函式呼叫wait或waitpid()等待子程式結束,又沒有顯式忽略該訊號,那麼它就一直保持僵死狀態,如果這時父程式結束了,僵死的子程式成為"孤兒程式",過繼給1號程式init,init始終會負責清理僵死程式,它產生的所有僵死程式也跟著消失(每個程式結束的時候,系統都會掃描當前系統中所執行的所有程式, 看有沒有哪個程式是剛剛結束的這個程式的子程式,如果是的話,就由Init來接管他,成為他的父程式)。但是如果如果父程式是一個迴圈,不會結束,那麼子程式就會一直保持僵死狀態,這就是為什麼系統中有時會有很多的僵死程式。怎麼檢視僵死程式,利用命令ps,可以看到有標記為Z的程式就是僵死程式。

 

查詢所有的僵死程式

 ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' 

 

二、僵死程式的危害
    如果父程式不呼叫wait/waitpid的話, 那麼保留的那段資訊就不會釋放,其程式號會一定被佔用,但是系統所能使用的程式號是有限的,如果產生了大量的僵死程式,將因為沒有可用的程式號而導致系統不能產生新的程式。 

 

三、僵死程式的避免 
1、父程式通過wait和waitpid等函式等待子程式結束,這會導致父程式掛起 
2、如果父程式很忙,那麼可以用signal函式為SIGCHLD安裝訊號處理函式。子程式結束後,父程式會收到該訊號,可以在訊號處理函式中呼叫wait回收 。
3、如果父程式不關心子程式什麼時候結束,那麼可以用signal(SIGCHLD, SIG_IGN)通知核心,自己對子程式的結束不感興趣,那麼子程式結束後,核心會回收, 並不再給父程式
傳送訊號。
或用sigaction函式為SIGCHLD設定SA_NOCLDWAIT,這樣子程式結束後,就不會進入僵死狀態
 struct sigaction sa; 
 sa.sa_handler = SIG_IGN; 
 sa.sa_flags = SA_NOCLDWAIT; 
 sigemptyset(&sa.sa_mask); 
 sigaction(SIGCHLD, &sa, NULL); 
4、fork兩次,父程式fork一個子程式,然後繼續工作,子程式fork一個孫程式後退出,那麼孫程式被init接管,孫程式結束後,init會回收。不過子程式的回收還要父程式來做。

  1.     int nStatus;
  2.     pid_t pid;
  3.     
  4.     pid = vfork();            //生成子程式
  5.     if (pid > 0)            //父程式
  6.     {
  7.         waitpid(pid, &nStatus, 0);    //等待子程式結束,否則子程式會成為僵死程式,一直存在,即便子程式已結束執行
  8.     }
  9.     else if (== pid)        //子程式
  10.     {
  11.         pid = vfork();        //生成孫程式
  12.         if (pid > 0) 
  13.         {
  14.             exit(0);        //子程式退出,孫程式過繼給init程式,其退出狀態也由init程式處理,與原有父程式無關
  15.         }
  16.         else if (== pid)    //孫程式
  17.         {
  18.             if (execlp("ls", "ls", NULL) < 0)
  19.             {
  20.                 perror("execlp");
  21.                 exit(-1);
  22.             }
  23.         }
  24.         else
  25.         { 
  26.             perror("vfork(child)"); 
  27.         } 
  28.     }
  29.     else
  30.     { 
  31.         perror("vfork(parent)"); 
  32.     } 
  33. }

相關文章