文章目錄
-
- wait()函數
- waitpid()函數
- wait() 和 waitpid() 用法和比較
wait()函數
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
程序一旦調用了wait,就立即阻塞自己,由wait自動分析是否目前程序的某個子程序已經退出,如果讓它找到了這樣一個已經變成僵屍的子程序,wait就會收集這個子程序的資訊,并把它徹底銷毀後傳回;如果沒有找到這樣一個子程序,wait就會一直阻塞在這裡,直到有一個出現為止。
參數:
- 參數status用來儲存被收集程序退出時的一些狀态,它是一個指向int類型的指針。但如果我們對這個子程序是如何死掉的毫不在意,隻想把這個僵屍程序消滅掉,(事實上絕大多數情況下,我們都會這樣想),我們就可以設定這個參數為NULL,就像這樣:
pid = wait(NULL);
傳回值:
- 如果成功,wait會傳回被收集的子程序的程序ID;
- 如果調用程序沒有子程序,調用就會失敗,此時wait傳回-1,同時errno被置為ECHILD。
waitpid()函數
#include <sys/wait.h>
#include <sys/types.h>
pid_t waitpid(pid_t pid, int *status, int options)
從本質上講,系統調用waitpid和wait的作用是完全相同的,但waitpid多出了兩個可由使用者控制的參數pid和options,進而為我們程式設計提供了另一種更靈活的方式。
參數:
- status:參數status用來儲存被收集程序退出時的一些狀态,它是一個指向int類型的指針。但如果我們對這個子程序是如何死掉的毫不在意,隻想把這個僵屍程序消滅掉,(事實上絕大多數情況下,我們都會這樣想),我們就可以設定這個參數為NULL,就像這樣:
pid = waitpid(-1, NULL, 0);
-
pid:從參數的名字pid和類型pid_t中就可以看出,這裡需要的是一個程序ID。但當pid取不同的值時,在這裡有不同的意義。
① pid>0時,隻等待程序ID等于pid的子程序,不管其它已經有多少子程序運作結束退出了,隻要指定的子程序還沒有結束,waitpid就會一直等下去(阻塞在waitpid()函數這裡);
② pid=-1時,等待任何一個子程序退出,沒有任何限制,此時waitpid和wait的作用一模一樣;
③ pid=0時,等待同一個程序組中的任何子程序,如果子程序已經加入了别的程序組,waitpid不會對它做任何理睬;
④ pid<-1時,等待一個指定程序組中的任何子程序,這個程序組的ID等于pid的絕對值;
- options:提供了一些額外的選項來控制
,目前在Linux中隻支援:waitpid
和WNOHANG
兩個選項,這是兩個常數,可以用WUNTRACED
運算符把它們連接配接起來使用;比如"|"
如果我們不想使用它們,也可以把options設為0,如ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果使用了ret=waitpid(-1,NULL,0);
參數調用WNOHANG
,即使沒有子程序退出,它也會立即傳回,不會像wait那樣永遠等下去。而waitpid
參數,由于涉及到一些跟蹤調試方面的知識,加之極少用到,可以自行查閱相關材料。WUNTRACED
-
傳回值:waitpid的傳回值比wait稍微複雜一些,一共有三種情況:
① 當正常傳回的時候,waitpid傳回收集到的子程序的程序ID;
② 如果設定了選項WNOHANG,而調用中waitpid發現沒有已退出的子程序可收集,則傳回0;
③ 如果調用中出錯,則傳回-1,這時errno會被設定成相應的值以訓示錯誤所在;當pid所訓示的子程序不存在,或此程序存在,但不是調用程序的子程序,waitpid就會出錯傳回,這時errno被設定為
;ECHILD
wait() 和 waitpid() 用法和比較
(一)
wait()
隻能處理一個子程序的
SIGCHLD
,如果多個并發子程序同時關閉,則會出現僵屍程序,而用
waitpid()
則會避免這種情況:
void sig_chld(int signo)
{
pid_t pid;
int stat;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) //非阻塞
printf("child %d terminated\n", pid);
return;
}
這段代碼展現在當有多個子程序結束并幾乎同時送出信号時,waitpid可使所有被送出的信号得到處理,而不會留下zombie。解決辦法:
- ① 輪詢wait傳回的pid,直到等待到所有的子程序pid;
- ② 使用waitpid偵聽特定的pid;
- ③ while(waitpid(-1, NULL, WNOHANGUP) > 0));進行一直地非阻塞等待,直至父程序回收完所有地結束子程序
如果使用信号:子程序結束發出信号,主程序偵聽wait處理所有信号-----所有5個信号結束信号SIGCHLD都在信号處理函數執行之前産生,而信号處理函數隻執行一次,因為Unix信号一般是不排隊的,如果是不同信号則進入不同的處理函數,但是同一信号隻能進入一次處理函數,後面的則丢失了。
其它: 調用 wait&waitpid 來處理終止的子程序
pid_t wait(int * status);
pid_t waitpid(pid_t pid,int *status, int options);
- 兩個函數都傳回兩個值:函數的傳回值和終止的子程序ID,而waitpid()子程序終止的狀态則是通過status指針傳回的;
- wait()函數等待第一個終止的子程序,而waitpid()函數則可以指定等待特定的子程序;
-
這的差別可能會在下面這種情況時表現得更加明顯:
當同時有5個客戶連上伺服器,也就是說有五個子程序分别對應了5個客戶,此時,五個客戶幾乎在同時請求終止,這樣一來,幾乎同時,五個FIN發向伺服器,同樣的,五個SIGCHLD信号到達伺服器,然而,UNIX的信号往往是不會排隊的,顯然這樣一來,信号處理函數将隻會執行一次,殘留剩餘四個子程序作為僵屍程序駐留在核心空間。此時,正确的解決辦法是利用waitpid(-1, &stat, WNOHANG)防止留下僵屍程序。其中的pid為-1表明等待任一個子程序,而WNOHANG選擇項通知核心在沒有已終止程序項時不要阻塞。
- wait()函數其實是waitpid()函數的特例,也即wait()函數是封裝waitpid()得來的。
static inline pid_t wait(int * wait_stat) {
return waitpid(-1, wait_stat, 0); //傳回值和錯誤
}
waitpid提供了wait函數不能實作的3個功能:
- ① waitpid等待特定的子程序, 而wait則傳回任一終止狀态的子程序;
- ② waitpid提供了一個wait的非阻塞版本;
- ③ waitpid支援作業控制。用于檢查 wait() 和 waitpid() 兩個函數傳回終止狀态的宏: 這兩個函數傳回的子程序狀态都儲存在status指針中,用以下3個宏可以檢查該狀态:
: 若為正常終止, 則為真。此時可執行 WEXITSTATUS(status): 取子程序傳送給exit或_exit參數的低8位.WIFEXITED(status)
: 若為異常終止, 則為真。此時可執行 WTERMSIG(status): 取使子程序終止的信号編号.WIFSIGNALED(status)
: 若為目前暫停子程序, 則為真。此時可執行 WSTOPSIG(status): 取使子程序暫停的信号編号WIFSTOPPED(status)