天天看點

wait()函數和waitpid()函數詳解

文章目錄

    • 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:提供了一些額外的選項來控制

    waitpid

    ,目前在Linux中隻支援:

    WNOHANG

    WUNTRACED

    兩個選項,這是兩個常數,可以用

    "|"

    運算符把它們連接配接起來使用;比如

    ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);

    如果我們不想使用它們,也可以把options設為0,如

    ret=waitpid(-1,NULL,0);

    如果使用了

    WNOHANG

    參數調用

    waitpid

    ,即使沒有子程序退出,它也會立即傳回,不會像wait那樣永遠等下去。而

    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個宏可以檢查該狀态:

    WIFEXITED(status)

    : 若為正常終止, 則為真。此時可執行 WEXITSTATUS(status): 取子程序傳送給exit或_exit參數的低8位.

    WIFSIGNALED(status)

    : 若為異常終止, 則為真。此時可執行 WTERMSIG(status): 取使子程序終止的信号編号.

    WIFSTOPPED(status)

    : 若為目前暫停子程序, 則為真。此時可執行 WSTOPSIG(status): 取使子程序暫停的信号編号

繼續閱讀