有關僵死進程和孤兒進程的小結

一、定義:什麼是孤兒進程和殭屍進程
   殭屍進程:一個子進程在其父進程還沒有調用wait()或waitpid()的情況下退出。這個子進程就是殭屍進程。

   孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,那麼那些子進程將成爲孤兒進程。孤兒進程將被init進程(進程號爲1)所收養,並由init進程對它們完成狀態收集工作。

    殭屍進程將會導致資源浪費,而孤兒則不會。

示例:

子進程持續10秒鐘的殭屍狀態(EXIT_ZOMBIE)
------------------------------------------------------

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

main()
{
    pid_t pid;
    pid = fork();
    if(pid < 0)
        printf("error occurred!/n");
    else if(pid == 0) {
        printf("Hi father! I'm a ZOMBIE/n");
        exit(0);      //(1)
    }
    else {
        sleep(10);
        wait(NULL);   //(2)
    }
}
(1) 向父進程發送SIGCHILD信號
(2) 父進程處理SIGCHILD信號

執行exit()時根據其父進程的狀態決定自己的狀態:
    如果父進程已經退出(沒有wait),則該子進程將會成爲孤兒進程過繼給init進程
    如果其父進程還沒有退出,也沒有wait(),那麼該進程將向父進程發送SIGCHILD信號,進入殭屍狀態等待父進程爲其收屍。如果父進程一直沒有執行wait(),那麼該子進程將會持續處於殭屍狀態。

子進程將成爲孤兒進程

------------------------------------------------------
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

main()
{
    pid_t pid;
    pid = fork();
    if(pid < 0)
        printf("error occurred!/n");
    else if(pid == 0) {
        sleep(6);
        printf("I'm a orphan/n");
        exit(0);
    }
    else {
        sleep(1);
        printf("Children Bye!/n");
    }
}
# ./a.out
Children Bye!
# I'm a orphan
(回車後將會進入#)
#

二、有什麼害處:
殭屍進程會佔用系統資源,如果很多,則會嚴重影響服務器的性能
孤兒進程不會佔用系統資源
處理流程:
只要老爹不等wait(sys/wait.h)兒子,兒子都將成爲孤魂野鬼zombie(zombie),unix中默認老爹總是想看兒子死後的狀態(以便報仇)  
  if   老爹比兒子先再見  
  兒子將被init(id   =   1)收養,最後的結果是zombie兒子徹底再見,系統資源釋放  
  else    
      {  
        兒子的zombie將一直存在,系統資源佔用...  
        if   老爹dead    
            兒子將被init(id   =   1)收養,最後的結果是zombie兒子徹底再見,系統資源釋放  
   
      else   類似的兒子zombie越來越多,系統就等死了!!!  
    }  
三、如何防止殭屍進程
首先明白如何產生殭屍進程:
1、子進程結束後向父進程發出SIGCHLD信號,父進程默認忽略了它
2、父進程沒有調用wait()或waitpid()函數來等待子進程的結束
第一種方法:  捕捉SIGCHLD信號,並在信號處理函數裏面調用wait函數
轉貼Richard Steven的Unix Network Programming代碼

int
main(int argc, char **argv)
{
                ...
        Signal(SIGCHLD, sig_chld);
                for(;
                }
                ...
}

void
sig_chld(int signo)
{
        pid_t        pid;
        int        stat;

        while ( (pid = waitpid(-1, &stat, WNOHANG)) >; 0)
                printf("child %d terminated/n", pid);
        return;
}

第二種方法:兩次fork():轉載
在《Unix 環境高級編程》裏關於這個在8.6節有非常清楚的說明。

實例
回憶一下8 . 5節中有關僵死進程的討論。如果一個進程要f o r k一個子進程,但不要求它等待子進程終止,也不希望子進程處於僵死狀態直到父進程終止,實現這一要求的訣竅是調用fork兩次。程序8 - 5實現了這一點。
在第二個子進程中調用sleep以保證在打印父進程I D時第一個子進程已終止。在fork之後,父、子進程都可繼續執行——我們無法預知哪一個會先執行。如果不使第二個子進程睡眠,則在fork之後,它可能比其父進程先執行,於是它打印的父進程I D將是創建它的父進程,而不是init進程(進程ID =1)。


#include        <sys/types.h>
#include        <sys/wait.h>
#include        "ourhdr.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/n", 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);
}
  //avoid zombie process by forking twice


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