python subprocess.Popen() returncode 0 || os.waitpid [Errno 10] No child processes
突然发现一个怪异的现象, 通过web发起的触发 subprocess.Popen() 调用命令行,returncode一直为0。在后台触发则是正常的,该为负则为负。
# 具体代码:
process = subprocess.Popen(cmd,
stdin=stdin,
stdout=stdout,
stderr=stderr,
cwd=cwd,
env=env,
shell=shell)
stdout, stderr = process.communicate(input=stdin)
return {
"retcode": process.returncode,
"stdout": stdout,
"stderr": stderr
}
把 subprocess.Popen() 换为os.system()后前后台都是正常的。
继续查subprocess.Popen() ,把命令行换成 "exit 1", 循环调用10,还有不一致的returncode。前面6次为1,后面4次为0。
后面把subprocess.Popen()换为subprocess.call(),经测试前后台也是正常的。查看了 subprocess的源代码 ,subprocess.call()就是subprocess.Popen()的封装呀。本质还是subprocess.Popen()。
仔细对比发现,区别在于subprocess.call()只是返回了wait()。而我们采用了更普适的方法process.communicate(input=stdin)。但为什么会这样呢?
修改subprocess.py代码进行debug,发现wait()函数里调用os.waitpid() 出现了 [Errno 10] No child processes异常。
def wait(self):
"""Wait for child process to terminate. Returns returncode
attribute."""
while self.returncode is None:
pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0)
except OSError as e:
if e.errno != errno.ECHILD:
raise
# This happens if SIGCLD is set to be ignored or waiting
# for child processes has otherwise been disabled for our
# process. This child is dead, we can't get the status.
pid = self.pid
sts = 0
# Check the pid and loop as waitpid has been known to return
# 0 even without WNOHANG in odd situations. issue14396.
if pid == self.pid:
self._handle_exitstatus(sts)
return self.returncode
代码运行到 sts=0 这里。以致returncode总为0。
经查询,gunicorn可能导致这种状况。我升级gunicorn到到python2.7能用的最新版本,还是没用。
我测试了我们python3改造完成后的版本,没有这问题。。
为了排除gunicorn等导致的问题,同事建议通过django原始的manage.py启动web,然后测试。
在启动通过python manager.py runserver 启动过程中就发现wsgi.py 里面报错了。
def wait_child(signum, frame):
while True:
cpid, status = os.waitpid(-1, os.WNOHANG)
if cpid == 0:
break
except OSError as e: