我遇到了一个奇怪的问题,因为我编写了一个脚本来启动我的本地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
时,如何终止整个子流程树?
答案 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
则没有。