Linux errno 与 Python

以下均为Linux环境测试。

起因:

开发的一个程序,经常会由于内存不足而被kill掉,使用的是os.system函数执行的,返回值总是35072,当时没多想。后来由于一些原因,要模拟OOM 被kill的状态,于是调用 sys.exit(35072) 突然发现,返回值不对。。。 于是有了下面的。

 

python中执行系统命令的方式一般来说有两种,os和subprocess(推荐)。

简单用法如下:

import os
os.system("ls")
# or
import subprocess
subprocess.call(["ls"])

 

本文重点在于此两种方式的返回值差异问题。

一、os.system

此函数返回值即为 os.wait 函数返回值中的 exit status,查看官方文档对 os.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

os.wait 返回一个元组,包含进程id和进程退出状态码。

敲重点:

进程退出码由两部分组成,分别是进程被kill的信号值和进程的退出码。

这两个数字合并为一个16位的数字,前8位是退出码,后8位是kill信号。

10进制计算方式:

进程退出码 = 进程退出码 * 2 ** 8 + kill信号值

测试:

python中可以调用sys.exit或者os._exit来模拟各种退出码

import os

cmd = "python -c 'import sys;sys.exit(1)'"
r = os.system(cmd)
print(r)  # r = 256

由于没有被kill,故kill信号值为0,所以 r = 1 * 2 ** 8 + 0 = 256

当进程被kill时,则没有退出码,故 r = kill信号值

tips:

使用 os.WEXITSTATUS 可将 os.system 返回值还原为退出码

二、subprocess.call

此函数返回的是subprocess.Popen对象的returncode属性,这个没找到文档,直接看源码吧。

def _handle_exitstatus(
    self,
    sts,
    _WIFSIGNALED=os.WIFSIGNALED,
    _WTERMSIG=os.WTERMSIG,
    _WIFEXITED=os.WIFEXITED,
    _WEXITSTATUS=os.WEXITSTATUS,
    _WIFSTOPPED=os.WIFSTOPPED,
    _WSTOPSIG=os.WSTOPSIG,
):
    """All callers to this function MUST hold self._waitpid_lock."""
    # This method is called (indirectly) by __del__, so it cannot
    # refer to anything outside of its local scope.
    if _WIFSIGNALED(sts):
        self.returncode = -_WTERMSIG(sts)
    elif _WIFEXITED(sts):
        self.returncode = _WEXITSTATUS(sts)
    elif _WIFSTOPPED(sts):
        self.returncode = -_WSTOPSIG(sts)
    else:
        # Should never happen
        raise SubprocessError("Unknown child exit status!")

在源码中可发现,它对不同类型的退出码(或者叫退出码不太合适。。)做了判断,如果是被kill或被停止,则返回一个负值,如果是正常退出码,则原样返回。

测试:

import subprocess

r = subprocess.call(["python", "-c", "import sys;sys.exit(1)"])
print(r)  # r=1

在没有被kill时,r = 退出码 = 1

当被kill时,懒得写代码了,返回值是 -9、-15等

参见:Linux 信号signal处理机制

 

ps:

Linux errno 的详细说明可参见:

Linux errno详解

补充一个:

errno: 137 OOM killer 

 

参考文献:

C语言 unsigned与signed区别

https://docs.python.org/3.7/library/subprocess.html#subprocess.Popen.wait

https://docs.python.org/3.7/library/os.html#os.system

https://shapeshed.com/unix-exit-codes/

posted @ 2019-05-19 17:06 Mr.Dolphin 阅读(...) 评论(...) 编辑 收藏