11

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

由于 subprocess.call 是阻塞的,它不起作用。因此我使用 subprocess.Popen 来替换调用。然而,一旦孩子终止(链接),Popen 将生成僵尸(已失效)进程。

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

提前致谢

4

4 回答 4

18

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

  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-ing 并因此立即收集所有僵尸。

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

  4. 最后,您可以简单地定期收集这些进程。如果您正在使用该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。]

于 2013-05-29T09:12:43.237 回答
2

在终止或杀死一个进程后,操作系统等待父进程收集子进程状态。您可以使用进程的communicate() 方法来收集状态:

p = subprocess.Popen( ... )
p.terminate()
p.communicate()

请注意,终止进程允许该进程拦截终止信号并对其执行任何操作。这是至关重要的,因为 p.communicate() 是一个阻塞调用。

如果您不希望这种行为使用 p.kill() 而不是 p.terminate() 让进程不会拦截信号。

如果您想使用 p.terminate() 并确保进程自行结束,您可以使用 psutil 模块检查进程状态。

于 2016-01-13T18:28:52.687 回答
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 是非阻塞的,可以根据需要调用。

于 2015-03-19T04:34:02.217 回答
0

请查看http://docs.python.org/2/library/multiprocessing.html

它提供了一个与线程非常相似的 API。如果需要,您可以等待子进程退出。

于 2013-05-29T07:19:21.893 回答