天天看點

wait函數傳回值總結

之前在學習wait和waitpid函數的時候,就對使用宏WIFEXITED來檢查擷取的程序終止狀态産生過疑惑:一般我們在程式中是調用的exit或者_exit函數來退出的,那麼wait和waitpid函數擷取的終止狀态直接就是我們傳遞給exit的參數不就OK了嗎?

    後來了解到是我考慮簡單了,因為程式退出不僅僅隻有我們顯示地調用exit這麼簡單,還會有異常退出等情況,本文就對wait函數擷取的狀态做個總結!

    先來對wait status做個整體總結,一般我們通過wait status可以判定子程序發生了以下事件:

   (1)子程序通過傳遞一個整形參數給exit(或者_exit)而正常退出

   (2)子程序被一個信号終止

   (3)子程序被一個信号暫停(調用waitpid時指定WUNTRACED标志)

   (4)暫停的子程序被信号SIGCONT恢複(調用waitpid時指定WCONTINUED标志)

    平時我們說的程序termination status指的隻是前兩個wait status(可以通過$?來檢視程序的termination status)。

    那麼wait status是如何表示這些事件的呢? —— 具體如何表示的,不同的平台有不同的定義,因為POSIX并沒有對實作做出詳細的定義,這也是為什麼推薦使用宏來檢查wait status了,主要是考慮到程式可移植問題。本文針對x86平台32位。

wait函數傳回值總結

    通過上圖可以發現,雖然wait status是int型的,但實際上隻使用了它的低2個位元組。

    高8位用來記錄正常退出狀态,這也正解釋了為什麼程式退出狀态的範圍總是0~255。

    低8位用來記錄信号。

    好了,現在來看看這些宏具體是如何實作的吧!

    在/usr/include/i386-linux-gnu/sys/wait.h中

wait函數傳回值總結

在/usr/include/i386-linux-gnu/bits/waitstatus.h中

wait函數傳回值總結

接下來我們簡單分析下這幾個宏:

WIFEXITED/WEXITSTATUS:當程式是正常退出時則WIFEXITED(status)為真,這種情況下WEXITSTATUS(status)傳回子程序的退出狀态。

WIFEXITED最後可以簡寫為:

wait函數傳回值總結

#define WIFEXITED(status) (((status) & 0x7f) == 0)  

當WIFEXITED(status)為真則表示((status) & 0x7f)為0,意思是:程式退出不是信号導緻的退出,那麼就是正常退出了。

WEXITSTATUS(status)可以簡寫為:

wait函數傳回值總結

#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)  

先将低8位清零,然後右移8位,則取得高8位數值,即程式正常退出狀态。

WIFSTOPPED/WSTOPSIG:當子程序是因為被一個信号暫停而傳回時則WIFSTOPPED(status)為真,在這種情況下WSTOPSIG(status)傳回這個暫停子程序信号的編号。

WIFSTOPPED(status)可以簡寫為:

wait函數傳回值總結

#define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)  

當wait status低八位數值是0x7f時,則表明子程序是被信号暫停而傳回的。

WSTOPSIG(status)可以簡寫為:

wait函數傳回值總結

#define WSTOPSIG(status) (((status) & 0xff00) >> 8)  

可以發現,在這種情況下,WSTOPSIG(status)與WEXITSTATUS(status)取值方式是一樣的。

WIFCONTINUED:當一個暫停的子程序被信号SIGCONT喚醒而傳回狀态,則WIFCONTINUED(status)為真,否則為假。

WIFCONTINUED(status)可以簡寫為:

wait函數傳回值總結

#define WIFCONTINUED(status) ((status) == 0xffff)  

當wait status低兩個位元組數值為0xfff時,表明一個暫停的子程序被SIGCONT信号喚醒。

WIFSIGNALED/WTERMSIG/WCOREDUMP:當程式異常終止時WIFSIGNALED(staus)為真,這種情況下WTERMSIG(status)傳回終止程序的信号編号。并且程式異常終止時産生了core檔案的話,則WCOREDUMP(status)為真,否者為假。

WIFSIGNALED(status)可以簡寫為:

wait函數傳回值總結

#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這個數值。

這個宏還可以如下實作:

wait函數傳回值總結

#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))   

這樣就好了解了,既不是信号暫停,又不是正常退出,那麼肯定也不是被信号SIGCONT喚醒,那就肯定是被信号終止的情況了,哈哈。

WTERMSIG(status)可以簡寫為:

wait函數傳回值總結

#define WTERMSIG(status) ((status) & 0x7f)  

這個就不用解釋了吧

WCOREDUMP(status)可以簡寫為:

wait函數傳回值總結

#define WCOREDUMP(status) ((status) & 0x80)  

這個也很好了解,就是檢測第8位是否為1,是1,則生成core檔案,否則不生成。

綜上,可以發現APUE的pr_exit函數實作的不夠全,下面給個全面的如下:

wait函數傳回值總結

#include <stdio.h>  

#include <stdlib.h>  

#include <string.h>  

#include <sys/wait.h>  

void pr_exit(const char *msg, int status)  

{  

    if (msg)  

        printf("%s ", msg);  

    if (WIFEXITED(status)) {  

        printf("normal termination, exit status = %d\n", WEXITSTATUS(status));  

    } else if (WIFSIGNALED(status)) {  

        printf("abnormal termination, signal number = %d(%s)%s\n",  

                WTERMSIG(status), strsignal(WTERMSIG(status)),  

#ifdef  WCOREDUMP  

                WCOREDUMP(status) ? " (core file generated)" : "");  

#else  

                "");  

#endif  

    } else if (WIFSTOPPED(status)) {  

        printf("child stopped, signal number = %d(%s)\n",  

                WSTOPSIG(status), strsignal(WSTOPSIG(status)));  

    }  

#ifdef WIFCONTINUED  

    else if (WIFCONTINUED(status)) {  

        printf("child continued by SIGCONT signal\n");  

    else {             /* Should never happen */  

        printf("what happened to this child? (status=%x)\n",  

                                    (unsigned int) status);  

}  

PS:C标準對一個有符号數值并且是負數時的右移操作的說明是未實作的,我試了gcc,結果還是負數,對這塊了解的不是很多,還請諒解。

參考連結:

<a href="http://tsecer.blog.163.com/blog/static/15018172012323975152/" target="_blank">http://tsecer.blog.163.com/blog/static/15018172012323975152/</a>

<a href="http://www.cs.virginia.edu/pipermail/splint-discuss/2008-March/001136.html" target="_blank">http://www.cs.virginia.edu/pipermail/splint-discuss/2008-March/001136.html</a>

參考書籍:

《The Linux Programming Interface》