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?

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

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

WEXITSTATUS(status)可以简写为:

[html] view plain copy

print?

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

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

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

WIFSTOPPED(status)可以简写为:

[html] view plain copy

print?

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

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

WSTOPSIG(status)可以简写为:

[html] view plain copy

print?

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

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

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

WIFCONTINUED(status)可以简写为:

[html] view plain copy

print?

  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?

  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?

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

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

WTERMSIG(status)可以简写为:

[html] view plain copy

print?

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

这个就不用解释了吧

WCOREDUMP(status)可以简写为:

[html] view plain copy

print?

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

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

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

[cpp] view plain copy

print?

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

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》

时间: 2024-10-11 04:44:59

wait函数返回值总结的相关文章

以函数返回值做参数时,函数调用的顺序

环境:vs2013 在下面的代码中 1 //类似于下面的代码 2 3 foo(char*,char*,char*); 4 5 char* str ="A#B#C"; 6 7 foo(strtok(str,"#"),strtok(NULL,"#"),strtok(NULL,"#")); 预计让函数foo得到("A","B","C")的参数,程序编译的时候没问题,但是运行

函数指针与指针函数返回值的区别

指针函数是指带指针的函数,即本质是一个函数.函数返回类型是某一类型的指针定义: 类型标识符 *函数名(参数表)eg: int *f(x,y);函数指针是指向函数的指针变量,即本质是一个指针变量.int (*f) (int x); /* 声明一个函数指针 */ f=func; /* 将func函数的首地址赋给指针f */ 函数指针与指针函数返回值的区别,码迷,mamicode.com

对象做函数参数和函数返回值时,调用复制构造函数,构造函数,析构函数的情况

// 对象做函数参数和返回值.cpp : 定义控制台应用程序的入口点.//exit(0)表示正常退出程序,exit(0)表示异常退出 //在调用input时,编译器用对象A去创建了形参对象temp,调用了复制构造函数,对象A中的数据复制给了对象temp// 在input函数中,执行temp.set(s),为对象temp中数据成员str申请了动态储存空间,并设置了输入的字符串//并没有改变实参A中的数据成员str的储存空间,故在执行语句A.show()后输出的字符串并没有改变.在函数调用结束后 /

shell调用函数返回值深入分析

编写shell脚本过程中,我们经常会自定义一些函数,并根据函数的返回值不同来执行相应的流程,那么我们如何来获取函数的返回值呢? 首先shell中调用函数有两种方式: 第一种:value=`function_name [arg1 arg2 ......]` 或 第二种:function_name [arg1 arg2 ......] echo $? 这两种有什么区别呢? 举个例子来说: [[email protected] ~]# cat test.sh #!/bin/sh function aa

const修饰函数参数 const修饰函数返回值 const修饰成员函数

看到const 关键字,C++程序员首先想到的可能是const 常量.这可不是良好的条件反射.如果只知道用const 定义常量,那么相当于把火药仅用于制作鞭炮.const 更大的魅力是它可以修饰函数的参数.返回值,甚至函数的定义体. const 是constant 的缩写,"恒定不变"的意思.被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性.所以很多C++程序设计书籍建议:"Use const whenever you need". 1

string 中的 length函数 和size函数 返回值问题

string 中的 length函数 和 size函数 的返回值  (  还有 char [ ] 中 测量字符串的  strlen 函数 ) 应该是 unsigned int 类型的 不可以 和 -1 比较. 应尽量避免 unsigned int 类型 和 int类型 数据 的比较 .当unsigned int 类型 和 int类型 数据 比较 时 ,会 把int 类型 转换 为 unsigned int类型 .如果 int是负数 ,转换 为 unsigned int 会是 一个 很大 的正整数

C语言的函数返回值

一:背景 谈到C语言的函数返回值,可能会感觉很亲切,不就是一个函数返回值嘛,当初学C语言的时候早就学过了很easy嘛,我曾经也是这么想的.后来要上研究生了,研究生阶段搞得就是C,所以又重新开始学习C,学习C的过程中遇到了很多问题,在此博客中一一记录.实际过程中遇到的第一个问题自然就是函数返回值了.如果有人问你在一个函数中声明一个字符串数组,最后再return这个数组.这可以实现嘛?如果是问我我可能会毫不犹豫的说OK.那事实呢?由此本文诞生了...... 二:问题 先看几个实际的例子: #incl

将引用作为函数返回值的优缺点

格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }好处:在内存中不产生被返回值的副本:(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的.因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!注意事项:(1)不能返回局部变量的引用.这条可以参照Effective C++[1]的Item 31.主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态.(2)不能返回函数内部new

linux编程中接收主函数返回值以及错误码提示

程序A创建子进程,并调用进程B,根据不调用的不同情况,最后显示结果不同. #include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> #include <errno.h> int main() { pid_t pid, rpid; int stat; if ((pid = fork()) < 0) { perror("for

Linux Shell函数返回值

转:http://blog.csdn.net/ithomer/article/details/7954577 Shell函数返回值,一般有3种方式:return,argv,echo 1) return 语句shell函数的返回值,可以和其他语言的返回值一样,通过return语句返回.示例: [javascript] view plain copy print? #!/bin/bash - function mytest() { echo "arg1 = $1" if [ $1 = &q