瑞瑞哥的博客

关于os.waitpid()返回值不是子进程的退出状态码

关于os.waitpid()返回值不是子进程的退出状态码

背景

写了一段代码,目的是父进程fork出子进程,然后通过os.waitpid()等待子进程执行结束,并且获取子进程退出的状态码。大致实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
import os
import time

pid = os.fork()
if pid == 0:
print "before sleep"
time.sleep(3)
print "after sleep"
sys.exit(99)
else:
_, exitcode = os.waitpid(pid, 0)
print exitcode

然而这段代码执行过之后,明明子进程返回了99,但是发现父进程获取到的结果是25344。好像返回了一个不正确的状态码,这是怎么回事呢?

分析

通过查看Python的官方文档,可以得知,os.waitpid()wait() 的返回值是一样的(其实还有好几个差不多的),那么看一下wait方法的返回值,注意官方说法:

Wait for completion of a child process, and return a tuple containing its pid and exit status indication: a 16-bit number, whose low byte is the signal number that killed the process, and whose high byte is the exit status (if the signal number is zero); the high bit of the low byte is set if a core file was produced.

这类函数返回一个元组,第一个是我们等待的进程的pid,在本文中返回pid没有意义,因为我们是指定进程pid等待的,它只是把我们给定的入参又返回了(当然在其他例子里,也可以不指定某个具体的pid作为入参,这时候就有意义了,告诉你返回的进程具体是哪个)。

重点是第二个参数,这是一个编码过的数字,这个数字只有16位,低8位是杀死该子进程的信号编号,而高8位是退出状态(如果信号编号是0),其中低8位的最高位如果被置位,则表示产生了一个core文件。

结合文档,印证实例代码,可以发现我们返回值99正好对应25344,因为99 * 256 = 25344,说明99正好在第二字节上。

解决

看到这里就知道了,其实只要把返回的第二个参数,从中提取出我们要的子进程的exitcode即可。

因为返回都是正数或者0,所以解决方案没那么复杂。首先要知道低8位都是0,前面低8位的值是255,那么实际上可以这么写:

1
2
3
_, exitcode = os.waitpid(pid, 0)
real_exitcode = exitcode % 255
return real_exitcode

因此最后只要这么写就可以了:

1
2
3
_, exitcode = os.waitpid(pid, 0)
real_exitcode = exitcode >> 8
return real_exitcode

虽然代码随便写都能成功,但是有两点请注意:

  1. Python的整数长度是不限制的
  2. 虽然说计算退出状态码不涉及高位溢出,但是要知道有这个东西,具体请看补码的内容

参考

https://docs.python.org/2/library/os.html

https://blog.csdn.net/u013061183/article/details/78525807

https://www.cnblogs.com/now-fighting/p/3534185.html