之前在學習wait和waitpid函式的時候,就對使用巨集wifexited來檢查獲取的程序終止狀態產生過疑惑:一般我們在程式中是呼叫的exit或者_exit函式來退出的,那麼wait和waitpid函式獲取的終止狀態直接就是我們傳遞給exit的引數不就ok了嗎?
後來了解到是我考慮簡單了,因為程式退出不僅僅只有我們顯示地呼叫exit這麼簡單,還會有異常退出等情況(關於程式終止的討論請參考apue 8.5小節),本文就對wait函式獲取的狀態做個總結!
先來對wait status做個整體總結,一般我們通過wait status可以判定子程序發生了以下事件:
(1)子程序通過傳遞乙個整形引數給exit(或者_exit)而正常退出
(2)子程序被乙個訊號終止
(3)子程序被乙個訊號暫停(呼叫waitpid時需指定wuntraced標誌)
(4)暫停的子程序被訊號sigcont恢復(呼叫waitpid時需指定wcontinued標誌)
平時我們說的程序termination status指的只是前兩個wait status(shell中可以通過特殊引數$?來檢視程序的termination status)。
那麼wait status是如何表示這些事件的呢? —— 具體如何表示的,不同的平台有不同的定義,因為posix並沒有對實現做出詳細的定義,這也是為什麼推薦使用巨集來檢查wait status的原因,主要是考慮到程式可移植問題。本文針對x86平台32位。
通過上圖可以發現,雖然wait status是int型的,但實際上只使用了它的低2個位元組。
高8位用來記錄正常退出狀態,這也正解釋了為什麼程式退出狀態的範圍總是0~255。
低8位用來記錄訊號。
好了,現在來看看這些巨集具體是如何實現的吧!
在/usr/include/i386-linux-gnu/sys/wait.h中
在/usr/include/i386-linux-gnu/bits/waitstatus.h中
接下來我們簡單分析下這幾個巨集:
wifexited/wexitstatus:
當程式是正常退出時則wifexited(status)
為真,這種情況下wexitstatus(status)返回子程序的退出狀態。
wifexited最後可以簡寫為:
#define wifexited(status) (((status) & 0x7f) == 0)
當wifexited(status)為真則表示((status) & 0x7f)為0,意思是:程式退出不是訊號導致的退出,那麼就是正常退出了。
wexitstatus(status)可以簡寫為:
#define wexitstatus(status) (((status) & 0xff00) >> 8)
先將低8位清零,然後右移8位,則取得高8位數值,即程式正常退出狀態。
wifstopped/wstopsig:當子程序是因為被乙個訊號暫停而返回時則wifstopped(status)為真,在這種情況下wstopsig(status)返回這個暫停子程序訊號的編號。
wifstopped(status)可以簡寫為:
#define wifstopped(status) (((status) & 0xff) == 0x7f)
當wait status低八位數值是0x7f時,則表明子程序是被訊號暫停而返回的。
wstopsig(status)可以簡寫為:
#define wstopsig(status) (((status) & 0xff00) >> 8)
可以發現,在這種情況下,wstopsig(status)與wexitstatus(status)取值方式是一樣的。
wifcontinued:當乙個暫停的子程序被訊號sigcont喚醒而返回狀態,則wifcontinued(status)為真,否則為假。
wifcontinued(status)可以簡寫為:
#define wifcontinued(status) ((status) == 0xffff)
當wait status低兩個位元組數值為0xfff時,表明乙個暫停的子程序被sigcont訊號喚醒。
wifsignaled/wtermsig/wcoredump:
當程式異常終止時wifsignaled(staus)為真,這種情況下wtermsig(status)返回終止程序的訊號編號。並且程式異常終止時產生了core檔案的話,則wcoredump(status)為真,否則為假。
wifsignaled(status)可以簡寫為:
#define wifsignaled(status) (((signed char)((status) & 0x7f + 1) >> 1) > 0)
這個巨集的寫法是這些巨集當中最難理解的乙個,以下是我的簡單分析過程,對錯還請批評指正!
(status) & 0x7f 的範圍是0 ~ 127
(status) & 0x7f + 1 的範圍是0 ~ 128
那麼(signed char)((status) & 0x7f + 1) 的範圍是1 ~ 127 和乙個-128
由此推出:(signed char)((status) & 0x7f + 1) >> 1的範圍是0 ~ 63 和乙個-64
由此得出當wait status的低7位數值是0x7f時是不符合要求的,即巨集wifsignaled(status)的這種寫法其實是排除了wait status的低7位數值是0x7f的。因為當低7位是0x7f(第8位為0)時表示的是子程序被訊號暫停。其實現有的平台的訊號編號也沒有達到127的。
我們知道訊號編號是從1開始的(kill函式對訊號編號0有特殊的處理)。右移一位相當於除以2的操作,1除以2在程式中是等於0的,所有其中的加1操作其實很有技巧性,既利用了訊號編號從1開始這個特性,又排除了127這個數值。
這個巨集還可以如下實現:
#define wifsignaled(status) (!wifstopped(status) && !wifexited(status))
這樣就好理解了,既不是訊號暫停,又不是正常退出,那麼肯定也不是被訊號sigcont喚醒,那就肯定是被訊號終止的情況了,哈哈。
wtermsig(status)可以簡寫為:
#define wtermsig(status) ((status) & 0x7f)
這個就不用解釋了吧
wcoredump(status)可以簡寫為:
#define wcoredump(status) ((status) & 0x80)
這個也很好理解,就是檢測第8位是否為1,是1,則生成core檔案,否則不生成。
綜上,可以發現apue的pr_exit函式實現的不夠全,下面給個全面的如下:
#include #include #include #include void pr_exit(const char *msg, int status)
else if (wifsignaled(status)) else if (wifstopped(status))
#ifdef wifcontinued
else if (wifcontinued(status))
#endif
(unsigned int) status);}}
ps:c標準對乙個有符號數值並且是負數時的右移操作的說明是未實現的,我試了gcc,結果還是負數,對這塊了解的不是很多,還請諒解。
《the linux programming inte***ce》
recv函式返回值總結
函式原型 int recv socket s,char buf,int len,int flags 功能 不論是客戶還是伺服器應用程式都用recv函式從tcp連線的另一端接收資料。引數一 指定接收端套接字描述符 引數二 指明乙個緩衝區,該緩衝區用來存放recv函式接收到的資料 引數三 指明buf的長...
函式返回值
這裡接著探求被調函式的返回值是如何傳遞到主調函式中的,下面為c程式。add函式通過直接返回a b的,而add1函式先定義乙個區域性變數c,儲存a b的值,然後再返回c的值。其中a b可以當成乙個臨時變數,其結果是暫時存放在暫存器中的 eax 參照反匯程式設計序,可以看出函式的值並不是通過壓棧 基址定...
函式返回值
一般說來,函式中是可以進行區域性變數的返回的,不然豈不是全部要用全域性變數,如果使用了全域性變數,那還有必要進行返回嗎?那函式就沒有它存在的意義了!但是要注意了,這裡所謂的區域性變數的返回很有內涵,什麼樣的值才可以進行返回而不出錯?其實,只要遵守一句話即可 函式不能返回指向棧記憶體的指標!為什麼?因...