回收內核空間資源 wait函數 waitpid函數

摘要:本文主要講述內核空間資源的收回,介紹wait和waitpid函數的基本使用方法,以及它們之間的差異.

回收內核空間資源 wait和waitpid函數

    進程退出時釋放了用戶空間的資源,但是進程PCB並沒有釋放,這一工作顯然不是自己完成,而是由當前進程的父進程完成的.
    當一個進程正常退出或異常退出時,內核就向其父進程發送SIGCHLD信號.因爲子進程終止是一個異步事件,所以這種信號也是內核向父進程發的異步通知.父進程可以選擇忽略該信號(如果父進程設置了SA_NOCLDWAIT標誌位(SA_NOCLDWAIT:使父進程在它的子進程退出時不會收到 SIGCHLD 信號),或者提供一個該信號發生時即被調用執行的函數。父進程可以顯示地調用wait()函數和waitpid()函數來完成.

1. wait()和waitpid()函數等待子進程結束

函數定義:
    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t wait(int *status);
    pid_t waitpid(pid_t pid, int *status, int options);
函數說明:
    如果等待到任意一個子進程結束,將返回當前結束的子進程PID,同時將子進程退出時的狀態存儲在“_stat_loc"變量中.如果出錯返回-1,錯誤原因存儲在errno中.
    調用wait()函數的父進程,如果其所有的子進程都還在運行,則父進程會阻塞,等待任意一個子進程結束,回收該子進程的內核進程資源.如果父進程沒有任何子進程,則會出錯.
   如果進程由於接收到SIGCHLD信號而調用wait(),則可期望wait()會立即返回.但是如果在任意時刻調用wait(),則進程可能會阻塞.
參數status:
    參數status是一個整型指針.如果status不是空指針,則終止進程的終止狀態就放在它所指向的內存單元內.如果不關心終止進程的狀態,則可將參數指定爲空指針.

2. wait()和waitpid()函數的區別

(1)在一個子進程終止前,wait()使其調用者阻塞,而waitpid()有一個選項,可使調用者不阻塞.
(2)waitpid()並不等待其在調用之後的第一個終止子進程,它有若干個選項,可以控制它所等待的進程(下文會詳細介紹).
例子1:演示wait()函數的基本使用方式
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/errno.h>

int main()
{
        pid_t pid,wait_pid;
        int status;
        pid = fork();    //調用fork函數
        if(pid==-1)
        {
                printf("fork errno:%m\n");
        }
        if(pid==0)
        {
                printf("my pid is :%d\n",getpid());
                sleep(5);
                exit(EXIT_SUCCESS);   //正常退出
        }
        else
        {
                wait_pid = wait(&status);   //等待子進程結束
                if(WIFEXITED(status))       //調用WIFEXITED宏
                {
                        printf("wait on pid:%d,normal exit ,return value is:%4x\n",wait_pid,WEXITSTATUS(status));
                }
                else if(WIFSIGNALED(status))
                {
                        printf("wait on pid:%d,recive signal,return value is:%4x\n",wait_pid,WIFSIGNALED(status));
                }
        }
        return 0;
}

程序正常運行退出,輸出:

:my pid is :2573
:wait on pid:2573,normal exit ,return value is:   0

如果在程序運行後,向子進程發送一個信號,則輸出:
:my pid is :2573
:wait on pid:2573,normal exit ,return value is:   1

    在程序中還使用了WIFEXITED和WIFSIGNALED宏,其中宏WIFEXITED是用來判斷進程是否是正常退出,如果是,此宏值爲1.對於這種情況,可執行WEXITSTATUS(status),取子進程傳送給exit,_exit或_Exit參數的低8位.
    而宏WIFSIGNALED是用來判斷進程是否是因爲收到信號後而退出的,如果是,此宏值爲1. 對於這種情況,可執行WTERMSIG(status),取使子進程終止的信號編號.
    另外還有兩個宏與wait和waitpid所返回的終止狀態相關的.WIFSTOPPED和WIFCONTINUED.

3. waitpid()函數等待指定的子進程

    如果一個進程有幾個子進程,那麼只要有一個子進程結束終止,wait()函數就會返回.但是現在我們想等待某一個子進程而已,其他子進程結束我們不關心,該怎麼辦?用戶可以使用waitpid()函數來等待指定子進程結束.
定義:pid_t waitpid(pid_t pid, int *status, int options);
   其中,第一個參數爲進程PID值,對該值有一下的解析:
(1)pid==-1 等待任一子進程結束,此時waitpid()函數等價於wait()函數;
(2)pid>0   等待其進程ID與pid相等的子進程;
(3)pid==0  等待其組ID等於調用進程組ID的任一子進程;
(4)pid<-1  等待其組ID等於pid絕對值得任一子進程.

    第二個參數爲調用它的函數中某個變量地址,如果執行成功,則用來存儲結束進程的結束狀態.
    第三個參數爲等待選項,可以設置爲0,亦可爲WNOHANG(不阻塞等待)和WUNTRACED(報告狀態信息).如果options設置爲WNOHANG,而此時沒有子進程退出,將立即返回0,不會像wait()那樣永遠等待下去.否則返回子進程PID,並在參數STAT_LOC中獲取子進程的狀態.

這是兩個常數,可以用"|"運算符把它們連接起來使用,比如:
ret = waitpid(-1,NULL, WNOHANG|WUNTRACED);
如果我們不想使用它們,也可以把options設爲0,比如:
ret = waitpid(-1, NULL, 0);

waitpid的返回值

由於waitpid函數參數較多,返回值也是根據參數的情況而定,綜合分析一共有3種情況:
(1)如果執行成功正常返回,waitpid()返回子進程的PID;
(2)如果參數options設置了選項WNOHANG,而waitpid在執行時沒有已退出的子進程,則返回0;
(3)如果調用出錯,則返回-1,這時errno會被設置成相應的值以指示錯誤所在;當指定的進程或進程組不存在,或者參數pid指定的進程不是調用進程的子進程則出錯.


4. waipid()函數提供了wait()函數沒有的三個功能

(1)waitpid可等待一個特定的進程,而wait則返回任一終止子進程的狀態.
(2)waitpid提供了一個wait的非阻塞版本.又是用戶希望取得子進程的狀態,但不想阻塞.
(3)waitpid支持作業控制.

例子2:等待指定的子進程
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    pid_t chlid_pid, wait_pid;
    chlid_pid=fork();
    if(chlid_pid<0)  
        printf("Error occured on forking.\n");
    else if(chlid_pid==0)
    {  
        sleep(5);
        exit(0);
    }
    do
    {
        wait_pid=waitpid(chlid_pid, NULL, WNOHANG);   //不阻塞,直接返回
        if(wait_pid==0)
        {   
            printf("No child exited\n");
            sleep(1);
        }
    }while(wait_pid==0);    
    
    if(wait_pid==chlid_pid)
        printf("successfully release child %d\n", wait_pid);
    else
        printf("some error occured\n");
    return 0;
}

輸出:

:No child exited
:No child exited
:No child exited
:No child exited
:No child exited
:successfully release child 2538

    每隔一秒鐘,父進程就檢查一次當前是否有子進程退出.在前5秒鐘,由於沒有子進程退出,父進程調用waitpid(chlid_pid, NULL, WNOHANG)直接返回.子進程退出後,便可以處理其後事.

筆者:個人能力有限,只是學習參考...讀者若發現文中錯誤,敬請提出.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙築高臺,靜下心來,慢慢地沉澱---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

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