python 进程收到 sigterm 不退出是因为默认不处理该信号,需显式注册 signal.signal(signal.sigterm, handler);sigkill 不可捕获;子进程不自动继承 sigterm,需手动转发;信号只投递到主线程,多线程需协调退出。

Python 进程收到 SIGTERM 时为什么没退出?
因为 Python 默认不处理 SIGTERM——它只是让进程继续运行,操作系统等超时后强行发 SIGKILL。你写的 try/except KeyboardInterrupt 完全捕获不到这个信号。
必须显式注册信号处理器:
import signal
import sys
<p>def handle_sigterm(signum, frame):
print("Received SIGTERM, shutting down gracefully...")</p><h1>做清理:关闭文件、断开连接、保存状态...</h1><pre class='brush:python;toolbar:false;'>sys.exit(0)signal.signal(signal.SIGTERM, handle_sigterm)
-
signal.signal()必须在主线程中调用,子线程里注册无效 - 如果主逻辑卡在阻塞 I/O(如
time.sleep()、queue.get()),信号可能延迟送达,需配合signal.pause()或设为可中断(如用socket.settimeout()) - 不能在信号处理器里做复杂操作:禁止调用
print(非异步信号安全)、不能启动新线程、避免分配内存
SIGKILL 能不能被 Python 捕获或忽略?
不能。这是内核级强制终止,Python 解释器连执行任何代码的机会都没有。所有资源由 OS 直接回收,atexit、__del__、finally 全部失效。
立即学习“Python免费学习笔记(深入)”;
常见误判场景:
- 看到进程“突然消失”,日志没留下任何痕迹 → 很可能是
kill -9或 OOM Killer 杀的 - Docker 容器被
docker kill默认发SIGKILL,不是SIGTERM;要用docker stop才先发SIGTERM - Linux 的
/proc/sys/vm/oom_kill_enable开启时,内存不足会直接发SIGKILL,无法拦截
用 subprocess 启动子进程时,SIGTERM 会传递过去吗?
不会自动传递。父进程收到 SIGTERM 后,子进程还在跑,变成孤儿进程(被 init 或 systemd 收养),可能持续占用资源。
正确做法是手动转发:
import subprocess import signal import os <p>proc = subprocess.Popen(["python", "child.py"])</p><p>def handle_sigterm(signum, frame): proc.terminate() # 发 SIGTERM 给子进程 try: proc.wait(timeout=5) except subprocess.TimeoutExpired: proc.kill() # 超时则降级为 SIGKILL sys.exit(0)</p><p>signal.signal(signal.SIGTERM, handle_sigterm)
-
proc.terminate()发的是SIGTERM,不是SIGKILL;要确保子进程自己也处理了该信号 -
proc.kill()是 Python 封装的SIGKILL,不可捕获,慎用 - 如果子进程是 shell 启动的(如
shell=True),proc.terminate()只杀 shell,不杀其子进程;要用preexec_fn=os.setsid配合os.killpg()
多线程 Python 程序里处理 SIGTERM 有什么坑?
信号只递送到主线程,其他线程收不到。如果你的主逻辑在子线程里跑,signal.signal() 注册了也没用。
- 必须保证主线程空闲等待信号,比如用
signal.pause()或time.sleep(3600) - 不要在子线程里调用
signal.signal(),会报ValueError: signal only works in main thread - 若用
threading.Event协作退出,主线程收到信号后设置事件,再join()子线程——但要注意子线程是否在阻塞调用中(如socket.accept()),需设 timeout 或用select中断
信号处理真正难的不是写几行 signal.signal(),而是判断哪些资源必须同步清理、哪些 I/O 调用会阻塞信号、以及当子进程或线程不配合时,你手里的超时和兜底手段够不够用。









