殭屍進程及其避免

怎樣避免產生殭屍進程
摘錄於: <<Advanced Programming in the UNIX® Environment: Second Edition>> By W. Richard Stevens, Stephen
1.什麼是殭屍進程?

In UNIX System terminology, a process that has terminated,but whose

parent has not yet waited for it, is called a zombie.

在UNIX 系統中,一個進程結束了,但是他的父進程沒有等待(調用wait / waitpid)他,

那麼他將變成一個殭屍進程.

但是如果該進程的父進程已經先結束了,那麼該進程就不會變成殭屍進程,

因爲每個進程結束的時候,系統都會掃描當前系統中所運行的所有進程,

看有沒有哪個進程是剛剛結束的這個進程的子進程,如果是的話,就由Init

來接管他,成爲他的父進程,從而保證每個進程都會有一個父進程.

而Init進程會自動wait 其子進程,因此被Init接管的所有進程都不會變成殭屍進程.

2. 殭屍進程的危害

由於子進程的結束和父進程的運行是一個異步過程,即父進程永遠無法預測子進程

到底什麼時候結束. 那麼不會因爲父進程太忙來不及waid子進程,或者說不知道

子進程什麼時候結束,而丟失子進程結束時的狀態信息呢?

不會.因爲UNIX提供了一種機制可以保證 只要父進程想知道子進程結束時的狀態信息,

就可以得到. 這種機制就是:

在每個進程退出的時候,內核釋放該進程所有的資源,包括打開的文件,佔用的內存等.

但是仍然爲其保留一定的信息(包括進程號the process ID,退出狀態the termination

status of the process,運行時間the amount of CPU time taken by the process等),

直到父進程通過wait / waitpid來取時才釋放.

但這樣就導致了問題,如果你進程不調用wait / waitpid的話, 那麼保留的那段信息就不會

釋放,其進程號就會一定被佔用,但是系統所能使用的進程號是有限的,如果大量的產生

僵死進程,將因爲沒有可用的進程號而導致系統不能產生新的進程.

此即爲殭屍進程的危害,應當避免.

3.殭屍進程的避免

 1、父進程通過wait和waitpid等函數等待子進程結束,這會導致父進程掛起

2. 如果父進程很忙,那麼可以用signal函數爲SIGCHLD安裝handler,因爲子進程結束後,

父進程會收到該信號,可以在handler中調用wait回收


3. 如果父進程不關心子進程什麼時候結束,那麼可以用signal(SIGCHLD, SIG_IGN)

通知內核,自己對子進程的結束不感興趣,那麼子進程結束後,內核會回收,

並不再給父進程發送信號

4. 還有一些技巧,就是fork兩次,父進程fork一個子進程,然後繼續工作,子進程fork一

個孫進程後退出,那麼孫進程被init接管,孫進程結束後,init會回收。不過子進程的回收

還要自己做。 下面就是Stevens給的採用兩次folk避免殭屍進程的示例.

Example

Recall our discussion in Section 8.5 about zombie processes. If we want to write a process so that it forks a child but we don't want to wait for the child to complete and we don't want the child to become a zombie until we terminate, the trick is to call fork twice. The program in Figure 8.8 does this.

We call sleep in the second child to ensure that the first child terminates before printing the parent process ID. After a fork, either the parent or the child can continue executing; we never know which will resume execution first. If we didn't put the second child to sleep, and if it resumed execution after the fork before its parent, the parent process ID that it printed would be that of its parent, not process ID 1.

Executing the program in Figure 8.8 gives us

   $ ./a.out
   $ second child, parent pid = 1

Note that the shell prints its prompt when the original process terminates, which is before the second child prints its parent process ID.

Figure 8.8. Avoid zombie processes by calling fork twice

 



#include 
"apue.h"
#include 
<sys/wait.h>

int
main(
void)
{
    pid_t   pid;

    
if ((pid = fork()) < 0{
        err_sys(
"fork error");
    }
 else if (pid == 0{     /* first child */
        
if ((pid = fork()) < 0)
            err_sys(
"fork error");
        
else if (pid > 0)
            exit(
0);    /* parent from second fork == first child */
        
/*
         * We're the second child; our parent becomes init as soon
         * as our real parent calls exit() in the statement above.
         * Here's where we'd continue executing, knowing that when
         * we're done, init will reap our status.
         
*/

        sleep(
2);
        printf(
"second child, parent pid = %d ", getppid());
        exit(
0);
    }

    
    
if (waitpid(pid, NULL, 0!= pid)  /* wait for first child */
        err_sys(
"waitpid error");

    
/*
     * We're the parent (the original process); we continue executing,
     * knowing that we're not the parent of the second child.
     
*/

    exit(
0);
}

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