添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
伤情的刺猬  ·  WSL 2 安装 Docker - 墨天轮·  1 年前    · 
机灵的西瓜  ·  [NewtonSoft.Json] ...·  1 年前    · 
开朗的大白菜  ·  ReactorClientHttpConne ...·  1 年前    · 

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: