在Python中终止子进程

2 投票
1 回答
1986 浏览
提问于 2025-04-16 09:10

根据我自己的调查完全重写

我有一个主脚本,它会运行其他几个 Python 脚本。这些脚本是这样创建的:

from subprocess import STDOUT, Popen
from signal import SIGINT
import shlex
p = Popen(shlex.split("python somescript.py arg1 arg2"), cwd="../src/somedir", stderr=STDOUT)

并且以以下方式结束:

p.send_signal(SIGINT)
p.wait()

在这些脚本内部,有以下代码:

if __name__ == "__main__":

    import signal
    def terminate(*args):
        raise KeyboardInterrupt
    signal.signal(signal.SIGINT, terminate)

    # do some work here

每个脚本都有一些功能,包含:

try:
    # create workers
except KeyboardInterrupt:
    # cleanup, wait for the current worker to end, then return

所有这些都按预期工作——主脚本创建了进程,当它执行结束时,会发送 SIGINT 信号,它们会以正确的方式处理这个信号,优雅地退出。

现在,我想以同样的方式运行 Django 开发服务器。

我修改了 manage.py 文件:

if __name__ == "__main__":
    import signal
    def terminate(*args):
        print 'got SIGINT'
        raise KeyboardInterrupt
    signal.signal(signal.SIGINT, terminate)

    execute_manager(settings)

execute_manager 函数经过多次调用,最终会调用一个 Django 命令方法,这个方法里有 except KeyboardInterrupt 块和 sys.exit(0)。所以,整个设置看起来是一样的。

问题是:Django 服务器实际上并没有停止,尽管我看到了 got SIGINT 的输出。

可能的解释:

看起来 Django 的 manage.py 会自己分叉,或者做了一些类似的事情;在查看活动监视器(macOS 的进程浏览器)时,我看到启动了 3 个 Python 进程——一个是主脚本,另外两个可能是 manage.py 的进程。当终止时,其中两个停止了(主脚本和我用 p 链接的那个),而第三个则继续运行,锁定了 8000 端口。有没有办法获取进程的子进程 PID?

1 个回答

0

你可以使用 psutil 来了解子进程的情况,比如在伪代码中:

p = Popen(...)
pp = psutil.Process(p.pid)
for child in pp.get_children():
    child.send_signal(signal.SIGINT)

注意在不使用 --reload 选项时,进程的不同,可以通过 ps -ef | grep manage.py | grep -v grep 来查看:

vinay 7864 7795  9 22:10 pts/0 00:00:00 python ./manage.py runserver
vinay 7865 7864 16 22:10 pts/0 00:00:00 /usr/bin/python ./manage.py runserver

与使用 --noreload 选项时的情况相比:

vinay 7874 7795  7 22:10 pts/0 00:00:00 python ./manage.py runserver --noreload

撰写回答