waitpid()设置WNOHANG位(非阻塞模式)判断子进程的状态是否有所改变

参考《Linux/Unix系统编程手册》26.1.5,对于系统调用waitid()

#include <sys/wait.h>

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

若在option中设置WNOHANG位,与那么该系统调用就是非阻塞的,也就是说会立刻返回而不是等待子进程的状态发生变化。

如果子进程的状态(正在运行or正常退出or被信号终止or被信号停止)发生变化,则会把状态具体信息保存在类型为siginfo_t结构的变量中。

PS:在sigaction处理信号时,也可以选用这个结构。

书上特意强调了一种情况,那就是,对于这种非阻塞操作假如子进程状态并未发生变化(也就是说正在运行,阻塞状态也可以算作未发生变化),infop指向的结构信息不会发生任何变化,因此需要预先memset将infop指向的结构每一位置为0,如果调用waitid()之后仍为0,则代表子进程状态未发生变化(简单来讲可理解为未退出)。

siginfo_t info;
// TODO:设置好waitid()的几个参数idtype、id、options
// ...
memset(&info, 0, sizeof(siginfo_t));
if (waitid(idtype, id, &info, options | WNOHANG) == -1)
    // TODO: 错误处理
    exit(1);
if (info.si_pid == 0) {
    // 子进程状态未发生改变
} else {
    // 子进程状态发生改变,状态信息存储到了info中
}

然后我就想到了,如果是使用waitpid()呢?

#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

waitpid()仅仅将状态保存在int变量status中,书上很多代码是像这样

int status;
if (waitpid(pid, &status, options) == -1) {
    // TODO: 错误处理
    exit(1);
}

这有个问题,就是status并没有进行初始化,以前写C++的时候,记住的一条准则就是别忘记给变量赋初值。但是发现在很多C代码中都没有这个行为,之前在哪里看过这有利于检查错误,因为往往未赋初值的变量会导致错误,于是一直就这么写下来了。

于是我在写了份测试代码:调用fork()创建进程,子进程休眠1秒就_exit(1)退出,父进程则立刻调用waitpid(childPid, &status, WNOHANG);

理论上父进程的waitpid返回0(调用成功),因为childPid是对的,但是status状态千奇百怪,后来发现原因就是:子进程状态未发生改变时,status的值不变,于是status就是系统默认初始值(并未确定)。

按照waitid()的思路,就是给status一个初值,然后检查调用waitpid()调用后status的值是否发生了变化。

问题就来了,status该设为什么值呢?

其实这里设为-1就行了。

那么有人会问,如果_exit(-1);退出呢?

退出码是个1字节的整型,而int至少占2个字节,也就是初始状态为11111111 11111111

而如果waitpid()得到的status为-1,其实是1个字节,转换成2个字节int后就是11111111 00000000(小端模式下),和原来的-1有所不同。

实验结果也证明了这点

第1个结果是子进程阻塞,status不变,因此状态status为最初的-1,未知

第2个结果是父进程先阻塞再调用waitpid(),此时子进程已经_exit(-1)退出了,状态status经过WEXITSTATUS转换后是255,即(unsigned char)-1。

posted @ 2017-07-12 20:40  Harley_Quinn  阅读(9257)  评论(0编辑  收藏  举报