Python lib psutil:如何使用wait()获取退出状态

时间:2013-12-17 02:43:22

标签: python subprocess exit-code

我正在使用带有许多参数的Subprocess Popen执行第三方应用程序

myprocess=Popen(['executionlist','with','arguments'],stdout=PIPE,stderr=PIPE)
myprocess.communicate()

获取stdout和err的元组,第三方应用程序在后台开始,所以我在stdout中得到它的pid ... [如果我尝试在forground中运行,它会抛出“错误打开终端:未知”] 我正在使用psutil跟踪所有这些分叉并使用

监视它
EXITCODE=psutil_obj.wait(timeout=xxx)

应用程序发送不同的退出代码,我需要在EXITCODE中访问,但是当我在不同的python脚本中运行它时,它总是给我“无”值....

按照, https://code.google.com/p/psutil/wiki/Documentation “Wait()用于进程终止,如果进程是当前进程的子进程,则还返回退出代码,否则返回None。”

无论如何我可以从独立进程ID访问退出代码,而不是由Popen专门分叉?

2 个答案:

答案 0 :(得分:4)

您引用的文档告诉您,psutil只能获取退出代码“如果流程是当前流程的子代”。

这就是真正的关键 - 不是这个过程是由Popen启动的,而是它是否是当前流程的孩子。*

这是Unix进程模型的基础。父母必须等待他们的孩子。你不能让别人等他们(除了重新定义他们)。如果父母仍然在运行,那么它必须是收获它们的父母。如果父级不再运行,则该子级已经是僵尸,或者它被重新分配给父级的父级或init / launchd / etc。或者获得孤儿(不同系统和不同情况下的细节不同)。没有其他过程可以等待它们的情况。

最重要的是,一旦父级调用waitPopen.communicate执行),进程及其在系统进程表中的条目及其retcode可能甚至不再存在。< / p>


*尽管如此,即使您没有跨过流程,将subprocessospsutil等较低级API混合也是一个坏主意。如果您创建了Popen对象,则必须调用其wait方法或其他为您执行此操作的方法communicate。一旦你这样做,它可能不再存在。如果要使用os.wait,请使用fork / spawn / etc创建的进程。 os中的方法。如果您要使用psutil.wait,则可以使用ospsutil.Popen创建的流程执行此操作,但不能使用subprocess.Popen

答案 1 :(得分:0)

在几条评论中来回反复,我怀疑你实际上是在寻找这样的事情:

在你的主程序中,“发射并忘记”一些后台进程。但是后来,在同一个程序中,每当每个进程实际完成时,运行一些代码来访问进程的返回代码(可能还有输出)。


这是一个通过对每个后台进程使用观察者线程来做到这一点的例子:

import subprocess
import threading

def doit(arglist, callback):
    def threadfunc():
        p = subprocess.Popen(arglist, 
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        out, err = p.communicate()
        callback(out, err, p.returncode)
    t = threading.Thread(target=threadfunc)
    t.start()

这有两个明显的局限性,但最终它们与您在尝试时遇到的完全相同的多路复用问题,例如,同时通过互联网与大量机器通信,并且它们具有相同的解决方案。

首先,拥有数百个线程是不好的。他们不是任何东西,但是他们仍然有大量的堆栈占用你的虚拟内存空间(特别是在32位Python中不好)并占用内核调度程序表中的空间(特别是在较旧的Unix系统)。幸运的是,如果您不关心Windows,可以像处理网络代码一样解决问题:将子流程管道抛入selectpoll循环。或者,如果您关心Windows,或者只是不想编写自己的select循环,那么找一个能够为您完成所有繁重任务的框架。如果您search PyPI for "subprocess",您会找到一些特定于subprocess的选项。如果您已经在使用事件驱动的网络或GUI框架(如Twisted或Qt),它可能有自己的方式来执行此操作(例如,请参阅Using Processes for Twisted)。

其次,你的callback在某个任意后台线程中被调用,除了通过改变一些共享值之外,无法将返回值或异常传播到其余代码。这意味着您现在正处理共享数据线程的所有常见问题,即使您从未要求这样做。改善这种情况的所有常用方法 - queueconcurrent.futures等 - 也适用于此处。


当然,如果你很幸运,你一次只能运行十几个进程(所以第一个问题不是问题),而你想要在回调中做的就是打印或记录一些数据(所以第二个问题不是问题)。