从Python中终止子进程及其子进程

23 投票
2 回答
19617 浏览
提问于 2025-04-15 21:36

我在使用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进程,而这个Java进程的ID在我的Python代码中是未知的。我试过用os.killpg来杀掉进程组,但这样会把运行这段代码的Python进程也杀掉。此外,由于其他原因,我也不能把shell设置为false,这样就避免了Java在shell环境中运行。

那么,我该如何终止shell和它生成的其他进程呢?

2 个回答

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(进程ID)传给你。这样做并不简单,而且情况也比较特殊。

34

要解决这个普遍的问题:

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

setsid 这个命令会让程序在一个新的会话中运行,这样就给它和它的子进程分配了一个新的进程组。这样,当你用 os.killpg 去结束这个进程组时,就不会影响到你自己正在运行的 Python 进程了。

撰写回答