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
$ 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/False
或preexec_fn=os.setsid/os.setpgrp
没有进行任何更改。
如果有人能帮助我理解这一点,我将不胜感激。谢谢你。
解决方案
这是一个竞争条件。
您正在分叉并立即发送信号,因此子进程在被杀死之前忽略它是一场竞赛。
此外,您的父脚本在检查脚本是否已死亡时具有竞争条件。您向脚本发出信号并立即检查它是否已死,因此在检查发生之前,这是一场让孩子死去的竞赛。
如果您time.sleep(1)
在发送信号之前添加 a,您将确保孩子赢得比赛,从而获得您期望的行为。
推荐阅读
- c - 我的 gets() 在我的代码中不起作用如何解决这个问题
- laravel - 找不到框“laravel/homestead”的“metadata.json”文件
- web-applications - 如何允许将表单数据发布到 Web 应用程序中的新窗口
- javascript - 在 Spotify SDK 上禁用自动播放
- oracle - OCIAttrGet/OCI_ATTR_STMT_TYPE 返回 16 ?
- c# - 如何使我的 UWP 应用程序像 Microsoft UWP 应用程序(邮件、Groove 音乐)一样快速?
- arrays - 如何使排序功能在 Perl 中运行良好?
- rocksdb - RocksDB:如何在 java 中使用 ttl?
- open-liberty - OpenAPI 多模块 EAR 部署
- php - 使用大长度数字时,增加一个值不起作用?