我想创建一个父进程,它将创建许多子进程。由于父进程负责创建子进程,因此父进程不会关心子进程的状态。
由于 subprocess.call 是阻塞的,它不起作用。因此我使用 subprocess.Popen 来替换调用。然而,一旦孩子终止(链接),Popen 将生成僵尸(已失效)进程。
有没有办法解决这个问题?
提前致谢
我想创建一个父进程,它将创建许多子进程。由于父进程负责创建子进程,因此父进程不会关心子进程的状态。
由于 subprocess.call 是阻塞的,它不起作用。因此我使用 subprocess.Popen 来替换调用。然而,一旦孩子终止(链接),Popen 将生成僵尸(已失效)进程。
有没有办法解决这个问题?
提前致谢
有很多方法可以解决这个问题。关键是存在僵尸/“失效”进程,以便父进程可以收集它们的状态。
作为流程的创建者,您可以宣布您忽略该状态的意图。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
-ing 并因此立即收集所有僵尸。
除了双分叉之外,您还可以使您的子进程存在于它们自己的单独会话中和/或放弃控制终端访问(“守护进程”,在 Unix-y 术语中)。(这有点混乱并且依赖于操作系统;我之前已经编写过代码,但是对于一些我现在无法访问的公司代码。)
最后,您可以简单地定期收集这些进程。如果您正在使用该subprocess
模块,.poll
只要看起来方便,只需在每个进程上调用该函数即可。None
如果进程仍在运行,这将返回,如果已完成,则返回退出状态(已收集它)。如果有些仍在运行,您的主程序可以在它们继续运行时退出;到那时,它们就会变成孤儿,就像上面的方法 #2 一样。
“忽略 SIGCHLD”方法简单易行,但缺点是会干扰创建和等待子进程的库例程。Python 2.7 及更高版本 ( http://bugs.python.org/issue15756 )中有一个解决方法,但这意味着库例程在这些子进程中看不到任何故障。
[编辑:http ://bugs.python.org/issue1731717是 for p.wait()
,从哪里来p
的进程subprocess.Popen
;15756 专门用于p.poll()
; 但是无论如何,如果您没有修复程序,则必须求助于方法 2、3 或 4。]
在终止或杀死一个进程后,操作系统等待父进程收集子进程状态。您可以使用进程的communicate() 方法来收集状态:
p = subprocess.Popen( ... )
p.terminate()
p.communicate()
请注意,终止进程允许该进程拦截终止信号并对其执行任何操作。这是至关重要的,因为 p.communicate() 是一个阻塞调用。
如果您不希望这种行为使用 p.kill() 而不是 p.terminate() 让进程不会拦截信号。
如果您想使用 p.terminate() 并确保进程自行结束,您可以使用 psutil 模块检查进程状态。
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 是非阻塞的,可以根据需要调用。
请查看http://docs.python.org/2/library/multiprocessing.html
它提供了一个与线程非常相似的 API。如果需要,您可以等待子进程退出。