1. 程式人生 > >【Linux 程序】孤兒程序、殭屍程序和守護程序

【Linux 程序】孤兒程序、殭屍程序和守護程序

1、孤兒程序:

孤兒程序:一個父程序退出,而它的一個或多個子程序還在執行,那麼那些子程序將成為孤兒程序。孤兒程序將被init程序(程序號為1)所收養,並由init程序對它們完成狀態收集工作。孤兒程序是沒有父程序的程序,孤兒程序這個重任就落到了init程序身上,init程序就好像是一個民政局,專門負責處理孤兒程序的善後工作。每當出現一個孤兒程序的時候,核心就把孤兒程序的父程序設定為init,而init程序會迴圈地wait()它的已經退出的子程序。這樣,當一個孤兒程序淒涼地結束了其生命週期的時候,init程序就會代表黨和政府出面處理它的一切善後工作。因此孤兒程序並不會有什麼危害

模擬孤兒程序的產生:

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

int main(void)
{
        pid_t pid ;
        signal(SIGCHLD,SIG_IGN);
        printf("before fork pid:%d\n",getpid());
        
int abc = 10; pid = fork(); if(pid == -1) { perror("tile"); return -1; } if(pid > 0) //父程序先退出 { abc++; printf("parent:pid:%d \n",getpid()); printf("abc:%d \n",abc); sleep(
5); } else if(pid == 0){ //子程序後退出,被託付給init程序 abc++; printf("child:%d,parent: %d\n",getpid(),getppid()); printf("abc:%d",abc); sleep(100); } printf("fork after...\n"); }

輸出:

before fork pid:27709
parent:pid:27709 
abc:11 
child:27710,parent: 27709
fork after...

然後我們使用命令:ps -ef檢視,PPID 為1的這個程序為上述程式產生的孤兒程序。

 

2、殭屍程序:

殭屍程序是當 子程序比父程序先結束,而父程序又沒有回收子程序,釋放子程序佔用的資源,此時子程序將成為一個殭屍程序。如果父程序先退出 ,子程序被init接管,子程序退出後init會回收其佔用的相關資源 我們都知道程序的工作原理。我們啟動一個程式,開始我們的任務,然後等任務結束了,我們就停止這個程序。 程序停止後, 該程序就會從程序表中移除。 在UNIX 系統中,一個程序結束了,但是他的父程序沒有等待(呼叫wait / waitpid)他, 那麼他將變成一個殭屍程序。 但是 如果該程序的父程序已經先結束了,那麼該程序就不會變成殭屍程序, 因為 每個程序結束的時候,系統都會掃描當前系統中所執行的所有程序, 看有沒有哪個程序是剛剛結束的這個程序的子程序,如果是的話,就由Init 來接管他,成為他的父程序…… 殭屍程序的危害: 由於子程序的結束和父程序的執行是一個非同步過程,即父程序永遠無法預測子程序到底什麼時候結束. 那麼會不會因為父程序太忙來不及wait子程序,或者說不知道子程序什麼時候結束,而丟失子程序結束時的狀態資訊呢? 不會。因 為UNⅨ提供了一種機制可以保證只要父程序想知道子程序結束時的狀態資訊, 就可以得到。這種機制就是: 在每個程序退出的時候,核心釋放該程序所有的資源,包括開啟的檔案,佔用的記憶體等。但是仍然為其保留一定的資訊(包括程序號the process ID,退出狀態the termination status of the process,執行時間the amount of CPU time taken by the process等)。直到父程序通過wait / waitpid來取時才釋放. 但這樣就導致了問題,如果程序不呼叫wait / waitpid的話,那麼保留的那段資訊就不會釋放,其程序號就會一直被佔用,但是系統所能使用的程序號是有限的,如果大量的產生僵死程序,將因為沒有可用的程序號而導致系統不能產生新的程序. 此即為殭屍程序的危害,應當避免模擬殭屍程序的產生:
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
int main(void)
{
        pid_t pid ;
        //signal(SIGCHLD,SIG_IGN);
        printf("before fork pid:%d\n",getpid());
        int abc = 10;
        pid = fork();
        if(pid == -1)
        {
                perror("tile");
                return -1;
        }
        if(pid > 0)
        {
                abc++;
                printf("parent:pid:%d \n",getpid());
                printf("abc:%d \n",abc);
                sleep(20);
        }
        else if(pid == 0){
                abc++;
                printf("child:%d,parent: %d\n",getpid(),getppid());
                printf("abc:%d",abc);
                exit(0);
        }
        printf("fork after...\n");

在父程序休眠的20秒內,產生了殭屍程序,此時可以開啟另一個終端,命令ps -ef檢視到:

admin 29696 29695 0 20:04 pts/1 00:00:00 [zombie] <defunct>

即我們模擬出來的殭屍程序。

避免殭屍程序的方式:

⒈ 父程序通過wait和waitpid等函式等待子程序結束,這會導致父程序掛起。 ⒉ 如果父程序很忙,那麼可以用signal函式為SIGCHLD安裝handler,因為子程序結束後, 父程序會收到該訊號,可以在handler中呼叫wait回收。 ⒊ 如果父程序不關心子程序什麼時候結束,那麼可以用 signal(SIGCHLD,SIG_IGN) 通知核心,自己對子程序的結束不感興趣,那麼子程序結束後,核心會回收, 並不再給父程序傳送訊號。 ⒋ 還有一些技巧,就是fork兩次,父程序fork一個子程序,然後繼續工作,子程序fork一 個孫程序後退出,那麼孫程序被init接管,孫程序結束後,init會回收。不過子程序的回收 還要自己做。   殺死殭屍程序的方法: 一般殭屍程序很難直接kill掉,不過您可以kill殭屍爸爸。 父程序死後,殭屍程序成為”孤兒程序”, 過繼給1號程序init,init始終會負責清理殭屍程序.它產生的所有殭屍程序也跟著消失。   3、守護程序: 守護程序: