kill 1234 默认发送SIGTERM(信号15),但是否优雅退出取决于进程是否注册并执行了信号处理逻辑;若未捕获,将按内核默认动作直接终止,导致资源泄漏、连接中断等问题。

直接用 kill 命令发 SIGTERM 就行,但前提是进程得真正“接住”它——否则和 kill -9 没区别,只是名义上优雅。
为什么 kill 1234 不一定优雅退出
因为 kill 默认发的是 SIGTERM(信号 15),但这个信号的处置行为由进程自己决定:exit(0)、忽略、阻塞、或执行自定义清理逻辑。如果进程没注册处理函数,就按内核默认动作终止——不等任何资源释放,也不写日志。
- 常见错误现象:
kill 1234后进程立刻消失,但数据库连接没关闭、临时文件没清理、socket 连接没close(),下游服务报 “connection reset” - 根本原因:程序启动时没调用
signal(SIGTERM, handler)或sigaction()注册回调;Go/Python 等语言里也常漏掉signal.signal(signal.SIGTERM, cleanup)这类注册 - 验证方法:用
strace -p 1234 -e trace=signal观察进程是否真的收到了SIGTERM并进入处理函数
kill -15 和 kill -s SIGTERM 有区别吗
没有本质区别,都是发 SIGTERM,但写法影响可读性和兼容性:
-
kill -15 1234:数字形式,所有 shell 都支持,但别人读脚本时得查表才知道是啥信号 -
kill -s SIGTERM 1234:符号名形式,语义清晰;但某些极老的 BusyBox 或 Alpine ash 可能不支持符号名,会报Invalid argument - 推荐在运维脚本里统一用
kill -s TERM 1234(TERM是SIGTERM的简写,POSIX 兼容)
怎样确认进程真正在响应 SIGTERM
不能只看进程是否退出,要看它有没有执行清理路径。关键检查点:
- 查日志:在信号处理函数里加一条
log("SIGTERM received, starting shutdown..."),然后tail -f /var/log/app.log观察是否输出 - 看资源残留:用
lsof -p 1234对比发信号前后打开的 socket、文件描述符数量;优雅退出后应归零或只剩标准流 - 测试超时行为:在清理函数里故意
sleep(10),再用timeout 5s kill -15 1234测试是否被强制截断——这暴露了 systemd 或容器 runtime 的超时设置是否压过了你的清理时间
Docker/Kubernetes 场景下 SIGTERM 容易被忽略的细节
容器环境里 SIGTERM 被吞掉不是代码问题,而是启动方式导致的:
- 如果 Dockerfile 用
CMD ["python", "app.py"],信号能正常传给python进程;但如果写成CMD sh -c "python app.py",sh会拦截SIGTERM,而不会转发给子进程(除非加exec) - Kubernetes 的
terminationGracePeriodSeconds默认 30 秒,但如果你的清理逻辑耗时 35 秒,K8s 会在第 30 秒补发SIGKILL——这时exit code 137就不是你代码的问题,而是配置没对齐 - systemd 服务里要用
KillMode=control-group,否则主进程退出后子进程可能继续跑,变成孤儿进程
真正的难点从来不在怎么发信号,而在于进程是否处于能接收并响应它的上下文——比如正在阻塞在 read()、持有锁、或运行在非主 goroutine 里。这些地方信号会被延迟甚至丢弃,得靠 signalfd、通道转发或异步通知机制绕过去。


















