Python:非阻塞+非解散过程

时间:2013-05-29 07:09:35

标签: python subprocess

我想创建一个父进程,它将创建许多子进程。由于父进程负责创建子进程,因此父进程不关心子进程的状态。

由于subprocess.call阻塞,它不起作用。因此我使用subprocess.Popen来替换call。然而,一旦孩子终止(Link),Popen将生成僵尸(已解散)过程。

有没有办法解决这个问题?

提前致谢

4 个答案:

答案 0 :(得分:14)

有很多方法可以解决这个问题。关键是存在僵尸/“失效”进程,以便父进程可以收集其状态。

  1. 作为流程的创建者,您可以宣布忽略状态的意图。 POSIX方法是设置标志SA_NOCLDWAIT(使用sigaction)。这在Python中有点痛苦;但大多数类Unix系统允许你简单地忽略SIGCHLD / SIGCLD(拼写从一个类Unix系统到另一个系统不同),这在Python中很容易做到:

    import signal

    signal.signal(signal.SIGCHLD, signal.SIG_IGN)

  2. 或者,如果由于某种原因这不可用或者在您的系统上不起作用,您可以使用旧的备用技巧:不要只分叉一次,分叉两次。在第一个孩子,叉第二个孩子;在第二个孩子中,使用execve(或类似的)来运行所需的程序;然后在第一个孩子中退出(_exit)。在原始父级中,使用waitwaidpid或操作系统提供的任何内容,并收集第一个孩子的状态。

    这样做的原因是第二个孩子现在变成了一个“孤儿”(它的父母,第一个孩子,死了,并且是由原始过程收集的)。作为一个孤儿,它被移交给代理父母(具体来说,“init”),它总是wait - 因此立即收集所有的僵尸。

  3. 除了双叉之外,您还可以使您的子流程在他们自己的单独会话中生效和/或放弃控制终端访问(“daemonize”,以Unix-y术语表示)。 (这有点混乱,依赖于操作系统;我之前编写过代码,但对于一些我现在无法访问的公司代码。)

  4. 最后,您可以定期收集这些流程。如果您正在使用subprocess模块,只需在每个进程上调用.poll函数,只要看起来方便。如果进程仍在运行,则返回None,如果已完成,则返回退出状态(已收集它)。如果有些仍然在运行,那么你的主程序可以在它们继续运行的同时退出;在那时,他们变成了孤儿,就像上面的方法#2一样。

  5. “忽略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是非阻塞的,可以根据需要调用。