分类:linux| 发布时间:2015-11-18 08:00:00
之前有人问我为什么当一个进程返回 1 时, waitpid 获得的 status 是 256 。 而在 shell 中:
echo $?
获得的是 1 这个正确的结果呢? 当时模糊的记得在 APUE 中有提到过相关的知识。 于是就说是因为进程结束 status 是分了几块的,某几个位代表了正常退出, 而另外几个位是代表异常退出。 shell 会处理结束状态将真正需要的结果存入 $? 中。
后来查了 APUE 以及相关文档(man waitpid)确认了我的想法是正确的。 同时写了一些 demo 来验证我的想法,于是便有了本文。
首先复习下 waitpid 的定义:
pid_t waitpid(pid_t pid, int *status, int options);
pid 为要等待的进程号,status 为返回的状态,而 options 在我们的例子中都是 0。
然后写几个用于测试进程结束状态的程序:
// return1.c
int main()
{
return 1;
}
// vim: set et ts=4 sts=4 sw=4:
这个例子非常简单,就是返回 1 。
// return2.c
int main()
{
return 2;
}
// vim: set et ts=4 sts=4 sw=4:
这个同样是简单的例子,返回 2 。
// sigfpe.c
int main()
{
int i = 3;
int j = 0;
int z = i / j;
return 0;
}
这是一个非正常退出的例子,会引发一个 SIGFPE 的异常。
接下来是一个执行程序并输出 waitpid 返回结果的例子:
// prexit.c
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void pr_exit(int status)
{
if (WIFEXITED(status)) {
printf("normal termination, exit status = %d\n",
WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("abnormal termination, signal number = %d%s\n",
WTERMSIG(status),
WCOREDUMP(status) ? " (core file generated)" : "");
} else if (WIFSTOPPED(status)) {
printf("child stopped, signal number = %d\n",
WSTOPSIG(status));
}
}
int main(int argc, char *argv[])
{
int status;
pid_t pid;
if (argc < 2) {
printf("command-line argument required\n");
exit(0);
}
if ((pid = fork()) < 0) {
printf("fork error\n");
exit(0);
} else if (pid == 0) {
if (execl(argv[1], argv[1], (char*)0) < 0) {
printf("execl error\n");
return 0;
}
}
if (waitpid(pid, &status, 0) < 0) {
printf("waitpid error\n");
return 0;
}
printf("status = %d\n", status);
pr_exit(status);
exit(0);
}
// vim: set et ts=4 sts=4 sw=4:
这个程序主要是通过 fork 和 execl 执行子进程,然后用 waitpid 得到其结束状态。 最后输出其结果。 需要注意的是,我们除了输出其结果外还在 pr_exit 中使用了 WIFEXITED、WIFSIGNALED 和 WIFSTOPPED 来判断子进程是正常退出还是异常退出。 如果是正常退出使用 WEXITSTATUS 来获取返回码,如果是异常退出使用 WTERMSIG 来获取信号值。
首先来看看执行在 shell 下执行上面三个测试程序的结果:
$ ./return1
$ echo $?
1
$ ./return2
$ echo $?
2
$ ./sigfpe
[1] 7191 floating point exception (core dumped) ./sigfpe
$ echo $?
136
可以看到在正常结束的情况下 echo $? 会返回正确的值。
再看看通过我们自己写的 prexit 程序获取到的结果:
$ ./prexit ./return1
status = 256
normal termination, exit status = 1
$ ./prexit ./return2
status = 512
normal termination, exit status = 2
$ ./prexit ./sigfpe
status = 136
abnormal termination, signal number = 8 (core file generated)
可以看到正常结束的情况下,返回 1 时,status 为 256。 返回 2 时,status 为 512 。
在 sys/wait.h 和 bits/waitstatus.h (在我的 Ubuntu 系统中,这个文件位于 /usr/include/x86_64-linux-gnu/ 不同系统可能不一样) 中可以看到 WIFEXITED WEXITSTATUS 等宏的实现。
比如:
// sys/wait.h
# define WTERMSIG(status) __WTERMSIG (__WAIT_INT (status))
# define WEXITSTATUS(status) __WEXITSTATUS (__WAIT_INT (status))
# define WCOREFLAG __WCOREFLAG
# define __WAIT_INT(status) (*(const int *) &(status))
// bits/waitstatus.h
#define __WEXITSTATUS(status) (((status) & 0xff00) >> 8)
#define __WTERMSIG(status) ((status) & 0x7f)
#define __WCOREFLAG 0x80
由此可以看到 status 主要有三部分组成:
| status | 0 0 0 0 0 0 0 0 | 0 | 0 0 0 0 0 0 0 |
| bits | 8 - 15 | 7 | 0 - 6 |
| mean | exit status | coredump generated | termination signal |
需要注意的是这是当调用 waitpid 时 第三个参数 options 为 0 时的情况。 如果此参数不为 0 ,情况会有所变化,具体请通过 man waitpid 查看。