我已经使用subprocess.check_output()
一段时间来捕获子进程的输出,但是在某些情况下遇到了一些性能问题。我在RHEL6机器上运行。
调用Python的环境是linux编译的64位环境。我正在执行的子进程是一个shell脚本,它最终通过Wine触发一个Windows python.exe进程(为什么需要这种愚蠢是另一回事)。作为shell脚本的输入,我正在输入一小段传递给Python.exe的Python代码。
当系统处于中等/高负载(40%到70%的CPU利用率)时,我注意到使用subprocess.check_output(cmd, shell=True)
可能会导致子进程在check_output命令返回之前完成执行后出现明显的延迟(最多45秒)。在此期间查看来自ps -efH
的输出,将调用的子进程显示为sh <defunct>
,直到它最终以正常的零退出状态返回。
相反,使用subprocess.call(cmd, shell=True)
在相同的中/重载下运行相同的命令将导致子进程立即返回,并且没有延迟,所有输出都打印到STDOUT/STDERR(而不是从函数调用返回)。
为什么只有当check_output()
将STDOUT/STDERR输出重定向到其返回值时才会有如此大的延迟,而不是当call()
简单地将其打印回父级的STDOUT/STDERR时呢?
阅读文档时,
subprocess.call
和subprocess.check_output
都是subprocess.Popen
的用例。一个小的区别是,如果子进程返回非零退出状态,check_output
将引发Python错误。关于check_output
(我的重点)的部分强调了更大的差异:那么
stdout
是如何“内部使用”的呢?让我们比较一下call
和check_output
:呼叫
检查输出
沟通
现在我们还要看
Popen.communicate
。这样做,我们注意到对于一个管道,communicate
所做的几件事比像call
那样简单地返回Popen().wait()
所花费的时间要长。首先,
communicate
处理stdout=PIPE
,不管您是否设置了shell=True
。显然,call
没有。它只会让你的壳喷出任何东西。。。使之成为一种安全风险,as Python describes here。其次,在
check_output(cmd, shell=True)
(只有一个管道)的情况下。。。子进程发送到stdout
的任何内容都由_communicate
方法中的线程处理。并且Popen
必须加入线程(等待它),然后再等待子进程本身终止!另外,更简单的是,它将
stdout
处理为list
,然后必须将其连接到字符串中。简而言之,即使参数最小,
check_output
在Python进程中花费的时间也比call
要多得多。让我们看看代码。.check_输出有以下等待:
.call使用以下代码等待:
请注意,这个bug与内部轮询有关。可在http://bugs.python.org/issue15756查看。几乎就是你遇到的问题。
编辑:在.call和.check_输出之间的另一个潜在问题是.check_输出实际上关心stdin和stdout,并将尝试对这两个管道执行IO。如果您正在运行的进程本身进入僵尸状态,则可能是对处于失效状态的管道的读取导致了您正在经历的挂起。
在大多数情况下,僵尸状态会很快被清除,但是,如果它们在系统调用(比如读或写)时被中断,它们就不会被清除。当然,一旦IO不能再执行,读/写系统调用本身就应该被中断,但是,有可能您遇到了某种竞争条件,在这种情况下,事情会以错误的顺序被终止。
在这种情况下,我能想到的唯一确定原因的方法是,要么向子进程文件中添加调试代码,要么调用python调试器,并在遇到所遇到的情况时启动回溯。
相关问题 更多 >
编程相关推荐