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

2024-06-02 08:52:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用子进程Popen执行一个第三方应用程序,其中有许多参数

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

在获取stdout和err的元组时,第三方应用程序在后台启动,因此我在stdout中获取其pid…[如果我尝试在forgound中运行,它将抛出“Error opening terminal:unknown”] 我用psutil来跟踪所有这些分叉并用

^{pr2}$

应用程序发送不同的退出代码,我需要在EXITCODE中访问这些代码,但是当我在不同的python脚本中运行它时,它总是给我一个'None'值。。。。在

根据, https://code.google.com/p/psutil/wiki/Documentation Wait()表示进程终止,如果该进程是当前进程的子进程,则返回退出代码,否则无

不管怎样,我能从独立进程id(不是Popen专门派生的)访问出口代码吗?在


Tags: 代码应用程序参数进程withstderrstdoutarguments
2条回答

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

这才是真正的关键所在–不是进程是否由Popen启动,而是它是否是当前进程的子进程。*

这是Unix进程模型的基础。父母必须等孩子。你不能让别人等他们(除非通过重新租用)。如果父母还在跑步,那就必须是收获它们的人。如果父级不再运行,则可能是子级已经是僵尸,或者它被重新租给父级的父级或init/launchd/etc.或者被孤立(在不同的系统和不同的情况下,细节是不同的)。没有其他进程可以等待它们的情况。在

除此之外,一旦父进程调用wait(这是Popen.communicate所做的),进程及其在系统进程表中的条目及其retcode可能不再存在。在


*也就是说,即使您没有跨进程,将subprocess与低级api(如os或{})混合使用也是个坏主意。如果创建一个Popen对象,则必须调用它的wait方法,或其他类似于communicate的方法。一旦你这样做了,它可能就不复存在了。如果要使用os.wait,请使用os中的fork/spawn/etc.methods创建的进程。如果要使用psutil.wait,可以使用由os或{}创建的进程,但不能使用subprocess.Popen。在

在反复讨论了一些评论之后,我怀疑你实际上是想做这样的事情:

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


下面是一个这样做的例子,通过对每个后台进程使用一个watcher线程:

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()

这有两个明显的局限性,但最终它们与您在尝试(例如,在internet上同时与多台计算机通信)时遇到的相同的多路复用问题,而且它们有相同的解决方案。在

首先,拥有数百个线程是不好的。它们不做任何事情,但它们仍然有很大的堆栈,占用了虚拟内存空间(在32位Python中尤其糟糕),并占用内核调度表中的空间(在旧unix中尤其糟糕)。幸运的是,如果您不关心Windows,您可以用处理网络代码的方法来解决这个问题:将子进程管道放入selectpoll循环中。或者,如果您不关心Windows,或者不想编写自己的select循环,请找到一个框架来为您完成所有的重担。如果您search PyPI for "subprocess",您将找到一些特定于subprocess的选项。如果您已经在使用一个事件驱动的网络或GUI框架,比如Twisted或Qt,那么它很可能有自己的方法来实现这一点(例如,请参见Twisted的Using Processes)。在

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


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

相关问题 更多 >