Performance of subprocess.check_output vs subprocess.call
我已经使用
调用Python环境是linux编译的64位。我正在执行的子进程是一个shell脚本,最终通过Wine触发Windows python.exe进程(为什么这个愚蠢是另一个故事)。作为shell脚本的输入,我在一小段Python代码中输入了传递给python.exe的代码。
虽然系统处于中等/重负载(CPU利用率为40%到70%),但我注意到在check_output命令之前子进程完成执行后,使用
相反,使用
为什么只有当
阅读文档,
The full function signature is largely the same as that of the Popen constructor, except that stdout is not permitted as it is used internally. All other supplied arguments are passed directly through to the Popen constructor.
那么
呼叫
1 2 | def call(*popenargs, **kwargs): return Popen(*popenargs, **kwargs).wait() |
check_output
1 2 3 4 5 6 7 8 9 10 11 12 | def check_output(*popenargs, **kwargs): if 'stdout' in kwargs: raise ValueError('stdout argument not allowed, it will be overridden.') process = Popen(stdout=PIPE, *popenargs, **kwargs) output, unused_err = process.communicate() retcode = process.poll() if retcode: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] raise CalledProcessError(retcode, cmd, output=output) return output |
通信
现在我们还要查看
首先,
其次,在
另外,更简单地说,它将
简而言之,即使参数最小,
我们来看看代码吧。 .check_output具有以下等待:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid, _WNOHANG=os.WNOHANG, _os_error=os.error, _ECHILD=errno.ECHILD): """Check if child process has terminated. Returns returncode attribute. This method is called by __del__, so it cannot reference anything outside of the local scope (nor can any methods it calls). """ if self.returncode is None: try: pid, sts = _waitpid(self.pid, _WNOHANG) if pid == self.pid: self._handle_exitstatus(sts) except _os_error as e: if _deadstate is not None: self.returncode = _deadstate if e.errno == _ECHILD: # 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. # http://bugs.python.org/issue15756 self.returncode = 0 return self.returncode |
.call使用以下代码等待:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | def wait(self): """Wait for child process to terminate. Returns returncode attribute.""" while self.returncode is None: try: 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 |
请注意与internal_poll相关的错误。可在http://bugs.python.org/issue15756查看。几乎就是你遇到的问题。
编辑:.call和.check_output之间的另一个潜在问题是.check_output实际上关心stdin和stdout,并将尝试对两个管道执行IO。如果您遇到一个自身进入僵尸状态的进程,则对处于已解除状态的管道进行读取可能会导致您遇到的挂起。
在大多数情况下,僵尸状态会很快得到清理,但是,如果他们在系统调用中被中断(例如读取或写入),则不会。当然,一旦IO不能再执行,读/写系统调用本身就会被中断,但是,你可能会遇到某种竞争条件,在这种情况下,事情会在错误的顺序中被杀死。
在这种情况下,我能想到确定哪个是原因的唯一方法是,您可以将调试代码添加到子流程文件中,或者在遇到遇到的情况时调用python调试器并启动回溯。