当前位置:首页 > 文章列表 > 文章 > linux > Linux 进程收到 SIGTERM 后仍不退出:信号处理、僵尸进程与优雅停止边界

Linux 进程收到 SIGTERM 后仍不退出:信号处理、僵尸进程与优雅停止边界

来源:17golang原创 2026-08-27 09:57:13 0浏览 收藏

发布脚本停服务时,终端已经打印了“收到 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 则是僵尸。最后一条命令如果列出多个子进程,就要继续判断父进程是否在等待它们。

Linux SIGTERM 从 kill 命令到进程处理函数的信号送达与状态检查

程序收到信号却不退,常见是处理函数没有收口

处理函数里只打印一行日志并不等于完成停止。比如下面的伪代码设置了标志,却没有让主循环观察这个标志,进程当然还会继续提供服务。

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 没有意义,修复点在它的父进程。

Linux 父进程通过 waitpid 回收子进程后僵尸状态消失的前后对比

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 是否在合理时间内看到服务结束。把这四个检查写进发布脚本或值班手册,下一次遇到“信号发了但进程还在”,就能快速区分等待、阻塞和僵尸,而不是反复发送更强的信号。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 测试资源怎么自动清理:t.Cleanup 注册时机与并行测试边界Go 测试资源怎么自动清理:t.Cleanup 注册时机与并行测试边界
上一篇
Go 测试资源怎么自动清理:t.Cleanup 注册时机与并行测试边界
医保亲情账户怎么绑定孩子:国家医保服务平台操作与限制
下一篇
医保亲情账户怎么绑定孩子:国家医保服务平台操作与限制
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    438次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    512次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    529次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    470次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    297次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码