【问题标题】:Why Python subprocesses won't properly capture signals?为什么 Python 子进程无法正确捕获信号?
【发布时间】:2021-02-18 13:42:06
【问题描述】:

让我们有一个很小的程序来捕获(并忽略)SIGTERM 信号:

# nosigterm.py:

import signal
import time

def ignore(signum, frame):
    print("Ignoring signal {}".format(signum))


if __name__ == '__main__':
  signal.signal(signal.SIGINT, ignore)
  signal.signal(signal.SIGTERM, ignore)

  while True:
    time.sleep(2)
    print("... in loop ...")

当从另一个 python 脚本作为子进程执行时,发送 SIGTERM 会终止这个子进程,我觉得这很奇怪:

# parent_script.py:

import signal
import subprocess
import sys

args = [sys.executable, "nosigterm.py"]
prog = subprocess.Popen(args)
assert prog.poll() is None

prog.send_signal(signal.SIGTERM)
print("prog.poll(): {}".format(prog.poll()))
assert prog.poll() is None, "Program unexpectedly terminated after SIGTERM"

输出是:

$ python3 parent_script.py 
prog.poll(): None
Traceback (most recent call last):
  File "parent_script.py", line 13, in <module>
    assert prog.poll() is None, "Program unexpectedly terminated after SIGTERM"
AssertionError: Program unexpectedly terminated after SIGTERM

你知道为什么会这样吗?

请注意,如果 nosigterm.py 作为独立的 Python 脚本 (python3 nosigterm.py) 执行,并且系统 kill 命令(在另一个终端中)发送 SIGTERM,它的行为应该如下:

$ python3 nosigterm.py 
... in loop ...
... in loop ...
Ignoring signal 15
... in loop ...
... in loop ...
... in loop ...

我尝试了三个 python 版本(2.7、3.6 和 3.7)和两个 Linux 操作系统(CentOS 7 和 Debian 9),结果都一样。如果我将nosigterm.py 替换为用C 编写的捕获SIGTERM 的二进制应用程序(通过sigaction()),行为仍然没有改变,因此它一定与父python 进程有某种关联。

另请注意,Popen 参数 restore_signals=True/Falsepreexec_fn=os.setsid/os.setpgrp 也没有进行任何更改。

如果有人能帮助我理解这一点,我将不胜感激。谢谢。

【问题讨论】:

  • FWIW,我可以在 macOS 上的 Python 3.8.2 中重现,但不能在同一台机器上的 Python 3.9 中重现。

标签: python linux subprocess signals posix


【解决方案1】:

这是一个竞争条件。

您正在分叉并立即发送信号,因此子进程在被杀死之前忽略它是一场竞赛。

此外,您的父脚本在检查脚本是否已终止时存在竞争条件。您向脚本发出信号并立即检查它是否已死亡,因此这是一场让孩子在检查发生之前死去的竞赛。

如果您在发送信号之前添加time.sleep(1),您将确保孩子赢得比赛,从而获得您期望的行为。

【讨论】:

  • 感谢您指出这一点!我确认在建议的位置添加 time.sleep(n) 可以解决这种情况。
猜你喜欢
  • 2022-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多