微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么Python子进程无法正确捕获信号?

如何解决为什么Python子进程无法正确捕获信号?

让我们有一个小小的程序,该程序应该捕获(并忽略)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,它的行为应为: / p>

$ 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),所有这些都具有相同的结果。如果我用用C编写的捕获SIGTERM的二进制应用程序(通过nosigterm.py代替sigaction(),则该行为仍然没有改变,因此它一定程度上与父python进程有关。

还要注意,Popen参数restore_signals=True/Falsepreexec_fn=os.setsid/os.setpgrp也没有做任何更改。

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

解决方法

这是比赛条件。

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

此外,您的父脚本在检查脚本是否已死亡时具有竞争条件。您向脚本发出信号,并立即检查脚本是否已死,因此这是孩子在检查之前死亡的竞赛。

如果在发送信号之前添加time.sleep(1),则可以确保孩子赢得比赛,并因此获得预期的行为。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。