Python避免了孤儿进程

时间:2014-10-11 12:09:47

标签: python subprocess

我使用python来测试某些东西。这可能需要很长时间,我想设置(全局)超时。我使用以下脚本(总结):

class TimeoutException(Exception):
    pass
def timeout_handler(signum, frame):
    raise TimeoutException()

# Halt problem after half an hour
signal.alarm(1800)
try:
    while solution is None:
        guess = guess()
        try:
            with open(solutionfname, 'wb') as solutionf:
                solverprocess = subprocess.Popen(["solver", problemfname], stdout=solutionf)
                solverprocess.wait()
        finally:
            # `solverprocess.poll() == None` instead of try didn't work either
            try:
                solverprocess.kill()
            except:
                # Solver process was already dead
                pass
except TimeoutException:
    pass
# Cancel alarm if it's still active
signal.alarm(0)

然而,它有时会产生孤儿进程,但我无法可靠地重现这种情况。有谁知道防止这种情况的正确方法是什么?

3 个答案:

答案 0 :(得分:2)

杀死进程后,您只需wait

答案 1 :(得分:2)

kill()方法的文档声明:

  

杀死孩子。在Posix操作系统上,该功能将SIGKILL发送给孩子。   在Windows上kill()terminate()的别名。

换句话说,如果您不在Windows上,则向子进程发送信号。 这将创建一个僵尸进程,因为父进程没有读取子进程的返回值。

kill()terminate()方法只是send_signal(SIGKILL)send_signal(SIGTERM)的快捷方式。

尝试在wait()之后添加对kill()的调用。这甚至显示在communicate()的文档中的示例中:

proc = subprocess.Popen(...)
try:
    outs, errs = proc.communicate(timeout=15)
except TimeoutExpired:
    proc.kill()
    outs, errs = proc.communicate()

请注意communicate()之后对kill()的来电。 (它相当于调用wait()并且还擦除了子进程的输出。)


我想澄清一件事:似乎你并不完全明白僵尸进程是什么。僵尸进程是一个终止进程。内核将进程保留在进程表中,直到父进程读取其退出状态。我相信子进程使用的所有内存实际上都是重用的;内核只需要跟踪这样一个进程的退出状态。

所以,你看到的僵尸进程没有运行。它们已经完全死了,这就是为什么它们被称为 zombie 。它们在进程表中“活着”,但根本没有运行。

调用wait()完成此操作:等到子进程结束并读取退出状态。这允许内核从进程表中删除子进程。

答案 2 :(得分:1)

在Linux上,您可以使用python-prctl

定义preexec函数,例如:

def pre_exec():
    import signal
    prctl.set_pdeathsig(signal.SIGTERM)

让你的Popen电话通过它。

subprocess.Popen(..., preexec_fn=pre_exec)

那很简单。如果父母去世,孩子的过程将会死亡,而不是孤儿。

如果你不喜欢python-prctl的外部依赖,你也可以使用较旧的prctl。而不是

prctl.set_pdeathsig(signal.SIGTERM)

你会有

prctl.prctl(prctl.PDEATHSIG, signal.SIGTERM)