進程調用 exit() 退出執行後,被設置為僵死狀態,這時父進程可以通過 wait4() 系統調用查詢子進程是否終結,之後再進行最後的操作,徹底刪除進程所占用的記憶體資源。 wait4() 系統調用由 linux 內核實現,linux 系統通常提供了 wait()、waitpid()、wait3()、 ...
進程調用 exit() 退出執行後,被設置為僵死狀態,這時父進程可以通過 wait4() 系統調用查詢子進程是否終結,之後再進行最後的操作,徹底刪除進程所占用的記憶體資源。 wait4() 系統調用由 linux 內核實現,linux 系統通常提供了 wait()、waitpid()、wait3()、wait4() 這四個函數,四個函數的參數不同,語義也有細微的差別,但是都返回關於終止進程的狀態信息。
1、wait() 函數:
wait() 函數的原型是:
#include <sys/types.h> // 提供類型 pid_t 的定義 #include <sys/wait.h> pid_t wait(int *status);
當進程調用 wait() 時,會暫停目前進程的執行(即阻塞),由 wait() 來自動分析是否當前進程的某個子進程已經退出,如果找到了這樣一個已經變成僵屍進程的子進程,wait 就會收集這個子進程的信息,並將其徹底銷毀後返回;如果沒有找到這樣一個子進程,wait 就會一直阻塞在這裡,直到出現僵屍進程。
參數 status 保存著子進程退出時的一些狀態(包括 task_struct、thread_info及內核棧等)它是一個指向 int 類型的指針;如果不在意子進程的結束狀態值,只想把這個僵屍進程消滅掉(實際上,大多數時候都是這樣做的),則可以將這個參數設為 NULL,即:
pid = wait(NULL); // 不管子進程的結束狀態,直接殺死進程
如果 wait() 調用成功,則會返回被收集子進程的進程ID;如果被調用進程沒有子進程,則調用失敗,返回 -1
接下來用一段代碼來演示一下 wait() 的用法:
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/wait.h> 6 7 void main(){ 8 pid_t fpid,rpid; 9 fpid = fork(); 10 if(fpid < 0){ 11 perror("error on forking!\n"); 12 } 13 else if(fpid == 0){ 14 printf("this is a child process! the pid is %d\n",getpid()); 15 sleep(3); 16 } 17 else{ 18 rpid = wait(NULL); // 如果 wait()調用成功,則返回子進程的PID;如果調用失敗,則返回 -1 19 printf("Catch the child process with pid of %d\n",rpid); 20 } 21 exit(0); 22 }
輸出結果如下:
關於 status 參數,比較複雜,暫時不做討論,可以參考這裡:https://www.ibm.com/developerworks/cn/linux/kernel/syscall/part3/index.html
2、waitpid() 函數:
函數原型:
#include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid,int *status,int options);
waitpid() 函數的功能與 wait() 的功能類似,不過,它比 wait() 函數多了兩個參數:
1)參數 pid 為欲等待的子進程的識別碼:
pid < -1 ;等待進程組 ID 為 pid 絕對值的進程組中的任何子進程;
pid = -1 ;等待任何子進程,此時 waitpid() 相當於 wait()。實際上,wait()就是 pid = -1、options = 0 的waitpid(), 且有:
static inline pid_t wait(*status){ return waitpid(-1,*status,0); }
pid = 0 ;等待進程組 ID 與當前進程相同的任何子進程(也就是等待同一個進程組中的任何子進程);
pid > 0 ;等待任何子進程 ID 為 pid 的子進程,只要指定的子進程還沒有結束,waitpid() 就會一直等下去。
2)參數 options 提供一些額外的選項來控制 waitpid():
WNOHANG;如果沒有任何已經結束了的子進程,則馬上返回,不等待;
WUNTRACED;如果子進程進入暫停執行的情況,則馬上返回,但結束狀態不予理會;
也可以將這兩個選項組合起來使用,使用 OR 操作。如果不想使用這兩個選項,也可以直接把 options 設為0 ,如下:
waitpid(-1,NULL,WNOHANG | WUNTRACED); // 沒有任何已結束了的子進程或子進程進入暫停執行的狀態,則馬上返回不等待 waitpid(-1,NULL,0); // options 設為0,則 waitpid() 會一直等待,直到有進程退出
3)waitpid() 的返回值,有三種:
a)正常返回時,waitpid() 返回收集到的子進程的PID;
b)如果設置了 WNOHANG,而調用 waitpid() 時,沒有發現已退出的子進程可收集,則返回0;
c)如果調用出錯,則返回 -1,這時erron 會被設置為相應的值以指示錯誤所在。(當 pid 所指示的子進程不錯在,或此進程存在,但不是調用進程的子進程, waitpid() 就會返回出錯,這時 erron 被設置為 ECHILD)
1 #include <sys/types.h>
2 #include <sys/wait.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 void main(){ 8 pid_t fpid,rpid; // fpid為fork()的返回值,rpid為waitpid()的返回值 9 fpid = fork(); 10 if(fpid < 0){ 11 printf("error on forking"); 12 } 13 else if(fpid == 0){ // 子進程中 fork() 返回值為0 14 printf("this is a child process,pid is %d\n",getpid()); 15 sleep(10); // 睡眠10s,10s 後子進程退出 16 exit(0); 17 } 18 do{ // 父進程中,fork()返回新創建子進程的 PID 19 rpid = waitpid(fpid,NULL,WNOHANG); // 等待 PID = fpid 的進程(即子進程)退出,設置了WNOHANG選項,表明當沒有發現已退出的子進程時不用等待直接返回,返回值為0; 20 if(rpid == 0){ // rpid = 0,說明沒有發現已退出的子進程 21 printf("No child exited\n"); 22 sleep(1); 23 } 24 }while(rpid == 0); 25 if(fpid == rpid) // 成功收集了退出的子進程,返回值為被收集子進程的PID 26 printf("successfully get child process %d\n",rpid); 27 else 28 printf("error!\n"); 29 }
結果如下:
從結果中可以看到,在子進程休眠的10s時間里,waitpid() 並沒有一直等待,而是直接返回0,然後做自己的事情(睡眠1s),如此重覆了10次;當子進程退出時,waitpid() 收集到退出的子進程,並返回所收集子進程的PID。
3、wait3()、wait4() 函數:
函數原型:
#include <sys/tpyes.h> #include <sys/wait.h> pid_t wait3(int *status,int options,struct rusage *rusage);
pid_t wait4(pid_t pid,int *status,int options,struct rusage *rusage);
wait3() 和 wait4() 函數除了可以獲得子進程狀態信息外,還可以獲得子進程的資源使用信息,這些信息是通過參數 rusage 得到的。而 wait3() 與 wait4() 之間的區別是,wait3() 等待所有進程,而 wait4() 可以根據 pid 的值選擇要等待的子進程,參數 pid 的意義與 waitpid() 函數的一樣。
本文主要參考:
https://www.ibm.com/developerworks/cn/linux/kernel/syscall/part3/index.html