• 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 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最后可以简写为:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #define WIFEXITED(status) (((status) & 0x7f) == 0)  

    当WIFEXITED(status)为真则表示((status) & 0x7f)为0,意思是:程序退出不是信号导致的退出,那么就是正常退出了。

    WEXITSTATUS(status)可以简写为:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #define WEXITSTATUS(status) (((status) & 0xff00) >> 8)  

    先将低8位清零,然后右移8位,则取得高8位数值,即程序正常退出状态。

    WIFSTOPPED/WSTOPSIG:当子进程是因为被一个信号暂停而返回时则WIFSTOPPED(status)为真,在这种情况下WSTOPSIG(status)返回这个暂停子进程信号的编号。

    WIFSTOPPED(status)可以简写为:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f)  

    当wait status低八位数值是0x7f时,则表明子进程是被信号暂停而返回的。

    WSTOPSIG(status)可以简写为:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #define WSTOPSIG(status) (((status) & 0xff00) >> 8)  

    可以发现,在这种情况下,WSTOPSIG(status)与WEXITSTATUS(status)取值方式是一样的。

    WIFCONTINUED:当一个暂停的子进程被信号SIGCONT唤醒而返回状态,则WIFCONTINUED(status)为真,否则为假。

    WIFCONTINUED(status)可以简写为:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #define WIFCONTINUED(status) ((status) == 0xffff)  

    当wait status低两个字节数值为0xfff时,表明一个暂停的子进程被SIGCONT信号唤醒。

    WIFSIGNALED/WTERMSIG/WCOREDUMP:当程序异常终止时WIFSIGNALED(staus)为真,这种情况下WTERMSIG(status)返回终止进程的信号编号。并且程序异常终止时产生了core文件的话,则WCOREDUMP(status)为真,否者为假。

    WIFSIGNALED(status)可以简写为:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #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这个数值。

    这个宏还可以如下实现:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status))   

    这样就好理解了,既不是信号暂停,又不是正常退出,那么肯定也不是被信号SIGCONT唤醒,那就肯定是被信号终止的情况了,哈哈。


    WTERMSIG(status)可以简写为:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #define WTERMSIG(status) ((status) & 0x7f)  

    这个就不用解释了吧

    WCOREDUMP(status)可以简写为:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #define WCOREDUMP(status) ((status) & 0x80)  

    这个也很好理解,就是检测第8位是否为1,是1,则生成core文件,否则不生成。

    综上,可以发现APUE的pr_exit函数实现的不够全,下面给个全面的如下:

    [cpp] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <string.h>  
    4. #include <sys/wait.h>  
    5.   
    6. void pr_exit(const char *msg, int status)  
    7. {  
    8.     if (msg)  
    9.         printf("%s ", msg);  
    10.   
    11.     if (WIFEXITED(status)) {  
    12.         printf("normal termination, exit status = %d ", WEXITSTATUS(status));  
    13.     } else if (WIFSIGNALED(status)) {  
    14.         printf("abnormal termination, signal number = %d(%s)%s ",  
    15.                 WTERMSIG(status), strsignal(WTERMSIG(status)),  
    16. #ifdef  WCOREDUMP  
    17.                 WCOREDUMP(status) ? " (core file generated)" : "");  
    18. #else  
    19.                 "");  
    20. #endif  
    21.     } else if (WIFSTOPPED(status)) {  
    22.         printf("child stopped, signal number = %d(%s) ",  
    23.                 WSTOPSIG(status), strsignal(WSTOPSIG(status)));  
    24.     }  
    25. #ifdef WIFCONTINUED  
    26.     else if (WIFCONTINUED(status)) {  
    27.         printf("child continued by SIGCONT signal ");  
    28.     }  
    29. #endif  
    30.     else {             /* Should never happen */  
    31.         printf("what happened to this child? (status=%x) ",  
    32.                                     (unsigned int) status);  
    33.     }  
    34. }  

    PS:C标准对一个有符号数值并且是负数时的右移操作的说明是未实现的,我试了gcc,结果还是负数,对这块了解的不是很多,还请谅解。

    参考链接:

    http://tsecer.blog.163.com/blog/static/15018172012323975152/

    http://www.cs.virginia.edu/pipermail/splint-discuss/2008-March/001136.html

    参考书籍:

    《The Linux Programming Interface》

  • 相关阅读:
    Python序列——字符串
    Python序列——序列操作
    Python数字
    Python对象
    Python基础
    关于PATH_INFO SCRIPT_NAME SCRIPT_FILENAME REDIRECT_URL 详解
    转:Nginx配置指令location匹配符优先级和安全问题
    转:mysql 创建一个用户,指定一个数据库
    【洛谷P3384】树链剖分
    【洛谷P1833】樱花
  • 原文地址:https://www.cnblogs.com/lidabo/p/5344779.html
Copyright © 2020-2023  润新知