Linux 进程收到 SIGTERM 后仍不退出:信号处理、僵尸进程与优雅停止边界
发布脚本停服务时,终端已经打印了“收到 SIGTERM”,但几秒后进程还在,systemd 又补了一次强制终止。这个现象通常不是 SIGTERM “失效”,而是信号送达、程序处理、子进程退出和父进程回收这四件事被混在了一起。先确认进程状态,再决定要改处理函数还是改停止流程,才能避免把正常的清理时间误判成死进程。
- SIGTERM 是请求进程自行收尾的信号,不保证立刻退出;SIGKILL 才不能被捕获或延后。
- 用
kill -TERM、ps -o pid,ppid,stat,wchan,cmd和日志分别确认送达、状态与阻塞点。 - 父进程退出前必须回收子进程,否则子进程可能暂时成为僵尸;僵尸不是“还在运行”,而是等待父进程读取退出状态。
- systemd 的
TimeoutStopSec只应给真实清理留时间,不能代替程序处理 SIGTERM。
SIGTERM 到底要求进程做什么
Linux 的 SIGTERM(信号编号 15)表达的是“请停止”。内核把信号放入目标进程的待处理队列,进程获得调度后才会进入默认动作或自定义处理函数。默认动作是终止进程;如果程序注册了处理函数,就可能先关闭监听套接字、刷新文件、通知子进程,然后再退出。
所以看到 kill -TERM 4182 返回成功,只能说明发送动作被接受,不能证明 PID 4182 已经退出。权限不足、PID 已变化、程序处于不可中断睡眠(D 状态)时,后续表现都不同。
先用三条命令确认卡在哪一层
假设服务主进程是 4182,先不要连续补发信号。下面三条命令分别回答“还在不在、是什么状态、父子关系怎样”。
kill -TERM 4182 ps -p 4182 -o pid,ppid,stat,wchan:24,etime,cmd ps --ppid 4182 -o pid,ppid,stat,etime,cmd
ps 查不到 PID,说明主进程已结束;仍能看到它时,重点看 STAT 与 WCHAN。S 或 R 多半是在等待处理或运行,D 常指不可中断的内核等待,Z 则是僵尸。最后一条命令如果列出多个子进程,就要继续判断父进程是否在等待它们。

程序收到信号却不退,常见是处理函数没有收口
处理函数里只打印一行日志并不等于完成停止。比如下面的伪代码设置了标志,却没有让主循环观察这个标志,进程当然还会继续提供服务。
static volatile sig_atomic_t stopping = 0;
static void on_term(int sig) {
(void)sig;
stopping = 1;
}
int main(void) {
signal(SIGTERM, on_term);
while (!stopping) {
serve_one_request();
}
close_listener();
return 0;
}
真实程序还可能卡在阻塞 I/O、锁等待或第三方库的关闭流程。日志只证明处理函数被调用,不证明 serve_one_request 已经返回。排查时把“收到信号”和“主循环结束”“监听套接字关闭”“子进程回收”分别打点,四个时间点才构成完整证据。
父进程退出前为什么必须回收子进程
子进程退出时,内核仍要保留 PID、退出码和资源使用量,等父进程调用 waitpid 或相关接口读取。父进程如果一直不读,子进程就会显示为 Z。它不再消耗 CPU,也不会继续执行,但 PID 表项仍被占用。
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /^Z/ {print}'
cat /proc/4182/task/4182/children
strace -p 4182 -e trace=wait4,waitid -tt
如果子进程先退出而父进程仍活着,父进程应处理 SIGCHLD 并循环调用 waitpid(-1, ..., WNOHANG),直到没有可回收的子进程。不要把僵尸直接当成“进程杀不掉”:对僵尸发送 SIGKILL 没有意义,修复点在它的父进程。

systemd 的停止流程与 SIGKILL 边界
由 systemd 管理时,systemctl stop demo.service 通常先向服务的控制组发送 SIGTERM,并等待 TimeoutStopSec。超时后才会按服务配置升级为 SIGKILL。可以用下面的命令核对实际配置和最近状态:
systemctl show demo.service -p KillMode -p TimeoutStopUSec -p ControlGroup systemctl status demo.service --no-pager journalctl -u demo.service -b --no-pager -n 80
KillMode=control-group 会把同一服务控制组里的进程一起纳入停止范围,适合主进程会创建工作进程的服务。无论采用哪种模式,程序都应该在 SIGTERM 中停止接收新任务、等待有限时间、回收子进程并退出;把 TimeoutStopSec 调得很大,只会延迟故障暴露。
一张表区分“慢退出”“阻塞”和“僵尸”
| 观察结果 | 含义 | 下一步 |
|---|---|---|
| 主 PID 消失,子进程也消失 | 停止已完成 | 核对端口和服务状态 |
| 主 PID 为 S/R,日志有 SIGTERM | 处理函数或清理流程仍在运行 | 查关闭阶段耗时与锁/I/O |
| 主 PID 为 D | 卡在不可中断内核等待 | 查磁盘、网络文件系统或驱动 |
| 子进程为 Z | 子进程已退,父进程未读取退出码 | 修复 waitpid/SIGCHLD 回收 |
容易误判的三个停止细节
不要用 kill -9 代替修复
SIGKILL 会立即终止目标,来不及刷新队列、删除临时文件或回收子进程。它适合明确失控且无法收尾的最后一步,不适合作为日常停止脚本的第一选择。
不要只看父进程的退出码
父进程返回 0 只说明它自己的收尾路径结束,仍需检查控制组、监听端口和子进程列表。服务重启很快时,旧 PID 消失也可能只是新实例已经换了 PID。
不要在处理函数里做长时间工作
信号处理函数应尽量设置状态、写入自管道或唤醒事件循环,把真正的清理放回主流程。否则处理函数自身阻塞,会让“已经收到信号”的日志掩盖真正的停机瓶颈。
相关问题
SIGTERM 和 SIGINT 有什么区别?
SIGTERM 通常由服务管理器或脚本发送,表示请求程序停止;SIGINT 常由终端的 Ctrl+C 产生。程序可以分别处理它们,也可以共用一套有界的收尾流程。
僵尸进程会不会继续占用内存?
僵尸已经结束,不再占用运行所需的用户态内存和 CPU,主要保留少量进程表信息。真正的风险是父进程长期不回收,导致 PID 表项逐渐耗尽。
什么时候应该调整 TimeoutStopSec?
先用日志和状态命令量出正常清理耗时,再给它留出有限余量。若耗时持续增长,应先处理锁、I/O 或子进程回收问题,而不是无限放大超时。
收尾检查
一次可靠的 Linux 优雅停止,至少要能回答四个问题:SIGTERM 是否送达,主循环是否停止,子进程是否退出并被回收,systemd 是否在合理时间内看到服务结束。把这四个检查写进发布脚本或值班手册,下一次遇到“信号发了但进程还在”,就能快速区分等待、阻塞和僵尸,而不是反复发送更强的信号。
Go 测试资源怎么自动清理:t.Cleanup 注册时机与并行测试边界
- 上一篇
- Go 测试资源怎么自动清理:t.Cleanup 注册时机与并行测试边界
- 下一篇
- 医保亲情账户怎么绑定孩子:国家医保服务平台操作与限制
-
- 文章 · linux | 4小时前 | Linux · 运维 · 磁盘管理 · tmpfiles.d · Linux 临时文件清理 tmpfiles.d 年龄阈值
- Linux 临时文件为什么会自动清理:tmpfiles.d 规则、年龄阈值与手动验证
- 126浏览 收藏
-
- 文章 · linux | 12小时前 | 容器 · Linux · 内存管理 · 故障排查 · Linux OOM memory.max memory.high cgroups v2
- Linux cgroups v2 内存限制怎么验证:memory.high、memory.max 与 OOM 事件逐项核对
- 304浏览 收藏
-
- 文章 · linux | 13小时前 | Linux · 日志排查 · 服务管理器 · Linux 服务管理器 journalctl 启动日志
- Linux journalctl 如何只看本次启动日志:-b、时间过滤与字段核对
- 484浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 5308次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 4821次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 4761次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 5027次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 4968次使用
-
- 详解golang执行Linuxshell命令完整场景下的使用方法
- 2023-01-07 426浏览
-
- go程序部署到linux上运行的实现方法
- 2023-01-02 387浏览
-
- Linux系统下Go语言开发环境搭建
- 2023-01-07 242浏览
-
- go调用shell命令两种方式实现(有无返回值)
- 2023-01-07 227浏览
-
- Go语言调用Shell与可执行文件的实现
- 2023-01-07 338浏览

