天天看點

Linux wait()、waitpid()實作程序同步

特殊程序

Linux系統中有兩種特殊程序:僵屍程序、孤兒程序;

僵屍程序:當程式調用exit()函數後,該程序并不是馬上就消失,
而是留下一個稱為僵屍程序的資料結構。僵屍程序是一種特殊的
程序,他幾乎放棄程序退出前占用的所有記憶體,既沒有可執行代
碼也不能被排程,隻在程序清單中留下一個位置,記載程序退出
狀态等資訊供父程序收集。若父程序中沒有回收子程序的代碼,
子程序會一直處于僵屍狀态。子程序退出後能釋放使用者區,但
不能釋放核心區的一些資料。

孤兒程序:父程序死了,子程序還或者,子程序就變成了孤兒程序。
此時,在較低的一些Linux版本,子程序會被init程序領養,但較高
的一些版本被一個特殊的程序領養;
           

特殊程序的危害

特殊程序不能再次運作。卻會占據一定的記憶體空間,當系統中的僵屍程序數量過多時,不僅會占據系統記憶體,還會占用程序id;是以使用wait()、waitpid()可以避免僵屍程序産生。

如果僵屍程序已經産生,通常解決僵屍程序的方法就是終止其父程序。這樣僵屍程序就會作為孤兒程序被init程序(現在的版本可能不是init程序領養,總之會有一個特殊的系統程序領養),這個特殊的程序會不斷調用wait()函數來擷取子程序的狀态,收集退出的子程序的狀态,并清理它占用的空間。最後,孤兒程序永遠不可能稱為僵屍程序;

wait()函數

傳回值:調用成功,傳回捕捉到的僵屍态子程序的id;調用失敗,傳回-1,errno被設定為ECHILD;

參數status:status是一個傳出參數,用于擷取子程序的退出狀态,如果不關心子程序的退出狀态可以傳入NULL;

調用wait()函數的程序會被挂起,進入阻塞狀态。直到子程序變成僵屍程序,wait()函數捕捉到子程序的退出狀态才會轉變到運作狀态,回收子程序的資源并傳回;若沒有變為僵屍态的子程序,wait()函數會讓程序一直阻塞。目前程序如果有多個子程序,隻要捕捉到一個變為僵屍态的子程序資訊,wait()函數就會傳回子程序id恢複執行狀态;

子程序的退出狀态被存放在exit()函數的status參數的低八位中。使用正常方法讀取比較麻煩,是以Linux定義了兩個宏,用于擷取程序退出狀态。

WIFEXITED(status);用于判斷子程序是否正常退出,如果是,傳回非零值,否則傳回零;

WEXITSTATUS(status);WIFEXITED()函數通常與WEXITSTATUS()函數搭配使用;若WIFEXITED()傳回非0(即正常退出),則可以使用WEXITSTATUS()提取子程序的傳回值;

WIFSIGNALED(status) 如果子程序因為一個未捕獲的信号而終止,它就傳回真;否則傳回假。

WTERMSIG(status) 如果WIFSIGNALED(status)為真,則可以用該宏獲得導緻子程序終止的信号代碼。

WIFSTOPPED(status) 如果目前子程序被暫停了,則傳回真;否則傳回假。

WSTOPSIG(status) 如果WIFSTOPPED(status)為真,則可以使用該宏獲得導緻子程序暫停的信号代碼。

waitpid()函數

pid>0時,隻等待pid與該參數相同的子程序,如果該子程序一直沒有退出,那麼父程序會一直阻塞;

pid=0時,會等待同一個程序組的子程序,若子程序加入了其他程序組,waitpid()不再關心它的狀态;

pid=-1時,waitpid()與wait()函數相同,将阻塞等待并回收一個子程序;

pid<-1時,會等待指定程序組的任何子程序,程序組的id等于pid的絕對值;

int options

參數options提供了一些另外的選項來控制waitpid()函數的行為。如果不想使用這些選項,則可以把這個參數設為0。

主要使用的有以下兩個選項:

WNOHANG 如果pid指定的子程序沒有結束,則waitpid()函數立即傳回0,而不是阻塞在這個函數上等待;如果結束了,則傳回該子程序的程序号;

WUNTRACED 如果子程序進入暫停狀态,則馬上傳回。

這些參數可以用“|”運算符連接配接起來使用。

如果waitpid()函數執行成功,則傳回子程序的程序号;如果有錯誤發生,則傳回-1,并且将失敗的原因存放在errno變量中。

失敗的原因主要有:沒有子程序(errno設定為ECHILD),調用被某個信号中斷(errno設定為EINTR)或選項參數無效(errno設定為EINVAL)

如果像這樣調用waitpid函數:waitpid(-1, status, 0),這此時waitpid()函數就完全退化成了wait()函數。

繼續閱讀