用Python结束一个子进程及其子进程

23

我正在使用Python 2.5的subprocess模块来生成一个Java程序(确切地说是Selenium服务器),代码如下:

import os
import subprocess

display = 0
log_file_path = "/tmp/selenium_log.txt"
selenium_port = 4455
selenium_folder_path = "/wherever/selenium/lies"

env = os.environ
env["DISPLAY"] = ":%d.0" % display
command = ["java", 
           "-server",
           "-jar", 
           'selenium-server.jar',
           "-port %d" % selenium_port]
log = open(log_file_path, 'a')
comm = ' '.join(command)
selenium_server_process = subprocess.Popen(comm,
                                           cwd=selenium_folder_path,
                                           stdout=log,
                                           stderr=log,
                                           env=env,
                                           shell=True)

这个进程应该在自动化测试完成后被结束。我正在使用 os.kill 来执行此操作:

os.killpg(selenium_server_process.pid, signal.SIGTERM)
selenium_server_process.wait()

这不起作用。原因是shell子进程会为Java生成另一个进程,而该进程的pid对我的Python代码来说是未知的。我尝试使用os.killpg杀死进程组,但这也会杀死运行此代码的Python进程。将shell设置为false以避免在shell环境中运行Java也不可行,因为有其他原因。

如何终止shell和由其生成的任何其他进程?


1
如何终止使用shell=True启动的Python子进程 - jfs
如果我只想杀死子进程怎么办? - Charlie Parker
2个回答

34

处理一般问题的方法:

p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), signal.SIGTERM)

setsid将在一个新会话中运行程序,因此为其及其子进程分配一个新的进程组。在其上调用os.killpg就不会导致您自己的Python进程崩溃。


不需要使用 setsid;你可以在 Python 中调用 os.setsid(https://dev59.com/pW445IYBdhLWcg3wl7TW#4791612)。 - jfs
不,你不能这样做...那会改变进程本身的会话,如果你想要的是仅杀死子进程,那不是你想要的。 - berdario
重新阅读问题的标题:“从Python中终止包括其子进程在内的子进程” - jfs
标题说:进程+子进程。你说:“只有子进程”。 - jfs
1
标题说“子进程+子进程”,我说“子进程”(意思是,子进程+子进程),您说“调用进程本身+子进程(子进程及其子进程)”。 - berdario
显示剩余4条评论

4
在这种情况下,明显的解决方案是不涉及shell
import os
import subprocess

display = 0
log_file_path = "/tmp/selenium_log.txt"
selenium_port = 4455
selenium_folder_path = "/wherever/selenium/lies"

env = os.environ
env["DISPLAY"] = ":%d.0" % display
command = ["java", 
           "-server",
           "-jar", 
           'selenium-server.jar',
           "-port",
           str(selenium_port)]
log = open(log_file_path, 'a')
selenium_server_process = subprocess.Popen(command,
                                           cwd=selenium_folder_path,
                                           stdout=log,
                                           stderr=subprocess.STDOUT,
                                           env=env)

这将使进程直接成为Java进程。请记住,它仍可能生成不属于进程组的进程,因此os.killpg仍然可能无法知道如何杀死它们。

如果您有理由调用shell(上述代码没有,而且几乎没有什么事情是您不能在没有shell的情况下完成的,但假设您确实需要),则必须使shell以某种方式传递启动的进程的pid。 这样做并不简单,而且相当情境化。


2
或者,如果您因任何原因涉及shell(比如您想要扩展/替换/等等),请在开头使用“exec”避免shell分叉。(具体来说,在“command”定义的开头添加“exec”就可以解决您的问题。) - moshez
2
你实际上有一个涉及shell的原因吗?很少会有这样的原因。 - Thomas Wouters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接