我想创建一个父进程,它将创建许多子进程。由于父进程负责创建子进程,因此父进程不关心子进程的状态。
由于subprocess.call阻塞,它不起作用。因此我使用subprocess.Popen来替换call。然而,一旦孩子终止(Link),Popen将生成僵尸(已解散)过程。
有没有办法解决这个问题?
提前致谢
答案 0 :(得分:14)
有很多方法可以解决这个问题。关键是存在僵尸/“失效”进程,以便父进程可以收集其状态。
作为流程的创建者,您可以宣布忽略状态的意图。 POSIX方法是设置标志SA_NOCLDWAIT
(使用sigaction
)。这在Python中有点痛苦;但大多数类Unix系统允许你简单地忽略SIGCHLD
/ SIGCLD
(拼写从一个类Unix系统到另一个系统不同),这在Python中很容易做到:
import signal
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
或者,如果由于某种原因这不可用或者在您的系统上不起作用,您可以使用旧的备用技巧:不要只分叉一次,分叉两次。在第一个孩子,叉第二个孩子;在第二个孩子中,使用execve
(或类似的)来运行所需的程序;然后在第一个孩子中退出(_exit
)。在原始父级中,使用wait
或waidpid
或操作系统提供的任何内容,并收集第一个孩子的状态。
这样做的原因是第二个孩子现在变成了一个“孤儿”(它的父母,第一个孩子,死了,并且是由原始过程收集的)。作为一个孤儿,它被移交给代理父母(具体来说,“init”),它总是wait
- 因此立即收集所有的僵尸。
除了双叉之外,您还可以使您的子流程在他们自己的单独会话中生效和/或放弃控制终端访问(“daemonize”,以Unix-y术语表示)。 (这有点混乱,依赖于操作系统;我之前编写过代码,但对于一些我现在无法访问的公司代码。)
最后,您可以定期收集这些流程。如果您正在使用subprocess
模块,只需在每个进程上调用.poll
函数,只要看起来方便。如果进程仍在运行,则返回None
,如果已完成,则返回退出状态(已收集它)。如果有些仍然在运行,那么你的主程序可以在它们继续运行的同时退出;在那时,他们变成了孤儿,就像上面的方法#2一样。
“忽略SIGCHLD”方法简单易行,但缺点是干扰创建和等待子进程的库例程。 Python 2.7及更高版本(http://bugs.python.org/issue15756)中有一个解决方法,但这意味着库例程无法在这些子流程中看到任何失败。
[编辑:http://bugs.python.org/issue1731717适用于p.wait()
,其中p
是来自subprocess.Popen
的流程; 15756专门针对p.poll()
;但无论如何,如果你没有修复,你必须采用方法2,3或4。]
答案 1 :(得分:1)
在终止或终止进程后,操作系统会等待父进程收集子进程状态。你可以使用这个过程' communic()方法收集状态:
p = subprocess.Popen( ... )
p.terminate()
p.communicate()
请注意,终止进程允许进程拦截终止信号并执行它想要做的任何事情。这是至关重要的,因为p.communicate()是一个阻塞调用。
如果你不希望这种行为使用p.kill()而不是p.terminate(),这会让进程不拦截信号。
如果要使用p.terminate()并确保进程自行结束,可以使用psutil模块检查进程状态。
答案 2 :(得分:0)
请查看http://docs.python.org/2/library/multiprocessing.html
它提供了一个与线程非常相似的API。如果需要,您可以等待子进程退出。
答案 3 :(得分:0)
torek的方法还可以!
我找到了另一种处理已解散过程的方法;
我们可以根据需要使用waitpid来回收已解散的流程:
import os, subprocess, time
def recycle_pid():
while True:
try:
pid, status, _ = os.wait3(os.WNOHANG)
if pid == 0:
break
print("----- child %d terminated with status: %d" %(pid, status))
except OSError,e:
break
print("+++++ start pid:", subprocess.Popen("ls").pid)
recycle_pid()
print("+++++ start pid:", subprocess.Popen("ls").pid)
recycle_pid()
time.sleep(1)
recycle_pid()
recycle_pid是非阻塞的,可以根据需要调用。