在KeyboardInterrupt上杀死一系列子进程

时间:2014-07-22 18:26:58

标签: python subprocess

我遇到了一个奇怪的问题,因为我编写了一个脚本来启动我的本地JBoss实例。

我的代码看起来像这样:

with open("/var/run/jboss/jboss.pid", "wb") as f:
    process = subprocess.Popen(["/opt/jboss/bin/standalone.sh", "-b=0.0.0.0"])
    f.write(str(process.pid))

    try:
        process.wait()
    except KeyboardInterrupt:
        process.kill()

理解起来应该相当简单,在运行时将PID写入文件,一旦我得到KeyboardInterrupt,就杀死子进程。

问题是JBoss在发送kill信号后继续在后台运行,因为似乎信号没有传播到由standalone.sh启动的Java进程。

我喜欢用Python编写系统管理脚本的想法,但是有很多奇怪的边缘情况,如果我用Bash编写它,一切都会有效。

当我得到KeyboardInterrupt时,如何终止整个子流程树?

1 个答案:

答案 0 :(得分:3)

您可以使用psutil库执行此操作:

import psutil

#..

proc = psutil.Process(process.pid)
for child in proc.children(recursive=True):
    child.kill()

proc.kill()

据我所知,subprocess模块不提供任何API函数来检索子进程生成的子代,os模块也没有。


杀死进程的更好方法可能是:

proc = psutil.Process(process.pid)
procs = proc.children(recursive=True)
procs.append(proc)

for proc in procs:
    proc.terminate()

gone, alive = psutil.wait_procs(procs, timeout=1)
for p in alive:
    p.kill()

这将使进程有机会正确终止,当超时结束时,其余进程将被终止。


请注意,psutil还提供了一个Popen类,其subprocess.Popen 的接口加上 psutil.Process的所有额外功能。您可能只想使用它而不是subprocess.Popen。它也是更安全的,因为psutil检查如果进程终止,PID不会被重用,而subprocess则没有。