如何将 SIGTERM 传播给通过子进程创建的子进程

How to propagate SIGTERM to children created via subprocess

给定以下 Python 脚本:

a.py:

#!/usr/bin/env python3
# a.py
import signal
import subprocess
import os

def main():
    print('Starting process {}'.format(os.getpid()))
    subprocess.check_call('./b.py')

if __name__ == '__main__':
    main()

b.py:

#!/usr/bin/env python3
# b.py
import signal
import time
import os

def cleanup(signum, frame):
    print('Cleaning up...')
    raise RuntimeError("Error")

def main():
    print('Starting process {}'.format(os.getpid()))
    signal.signal(signal.SIGINT, cleanup)
    signal.signal(signal.SIGTERM, cleanup)

    while True:
        print('Hello')
        time.sleep(1)

if __name__ == '__main__':
    main()

如果我执行 a.py,然后我通过 kill -15 <pid_of_a_py> 杀死它,它会杀死 a.py,但 b.py 保持 运行:

$ ./a.py 
Starting process 119429
Starting process 119430
Hello
Hello
Hello
Hello
Hello
Terminated   // On a separate terminal, I ran "kill -15 119429"
$ Hello
Hello
Hello
Hello

这是为什么?如何确保 SIGTERMa.py 传播到 b.py?还要考虑更深的链 a.py -> b.py -> c.py -> d.py ...我只想为最里面的脚本显式设置错误处理和清理。

一个解决方案是从 a.py

显式抛出 SystemExit
#!/usr/bin/env python3
# a.py
import signal
import subprocess
import os


def cleanup(signum, frame):
    raise SystemExit(signum)

def main():
    signal.signal(signal.SIGINT, cleanup)
    signal.signal(signal.SIGTERM, cleanup)
    print('Starting process {}'.format(os.getpid()))
    subprocess.check_call('./b.py')

if __name__ == '__main__':
    main()

或者,您可以使用 Popen 启动进程,并在父进程退出时调用 Popen.send_signal 到子进程。

编辑:

我已经阅读了一些有关该主题的资料,这是一种预期的行为。 kill -15 <pid> 将信号发送到指定的进程,并且只有这个进程,不应传播信号。但是,您可以向进程组发送信号,这也会杀死所有子进程。语法是 kill -15 -<pgid>(注意额外的破折号)。进程组 ID 通常与领导进程 ID 相同。