回收內核空間資源 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)直接返回.子進程退出後,便可以處理其後事.
筆者:個人能力有限,只是學習參考...讀者若發現文中錯誤,敬請提出.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------勿在浮沙築高臺,靜下心來,慢慢地沉澱---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------