登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux systemd.kill KillMode 怎样控制停止服务时的进程范围

来源:17golang原创

时间:2026-10-27 14:08:52 195浏览 收藏

如果一个 systemd 服务停止后仍有 worker、子进程或脚本残留,先看 KillMode=,不要只盯着 ExecStop=。它决定 systemd 停止 unit 时覆盖哪个进程范围:默认的 control-group 会处理整个控制组,mixed 会区分主进程与其余进程,process 只处理主进程,none 则不主动杀进程。

官方地址:https://www.freedesktop.org/software/systemd/man/latest/systemd.kill.html

常规服务优先保留默认的 KillMode=control-group;只有明确知道子进程生命周期由别的管理器接管时,才考虑更窄的范围。process 和 none 容易让进程脱离 systemd 的生命周期与资源管理。

先区分四种 KillMode 的进程范围

KillMode= 写在 unit 的 [Service] 等配置段中,核心不是“发什么信号”,而是“信号发给哪些对象”。官方文档列出四个取值:

取值停止时的覆盖范围适合怎样理解
control-group处理 unit 控制组中剩余的全部进程默认、最容易保持生命周期一致
mixed先对主进程发首个终止信号,最终信号覆盖控制组中的剩余进程主进程需要优雅退出,同时不放过子进程
process只处理主进程范围很窄,官方不推荐常规使用
none不主动杀进程,只执行停止命令极特殊场景,官方强烈不建议
systemd unit、KillMode、主进程、控制组与停止信号之间关系的静态结构图
图1:结构说明图,展示 KillMode 与主进程、控制组及停止信号的关系;这是静态说明图,不是运行截图。

这里的“控制组”可以理解为 systemd 为 unit 维护的进程边界。服务程序自己启动的 worker 不会因为它不是主进程就自动脱离这个边界;真正决定停止覆盖范围的是 unit 的 KillMode 和后续的超时处理。

用停止信号与超时规则理解最终结果

KillMode 只回答“覆盖谁”,停止过程还受到信号和超时配置影响。通常 systemd 会先发送 SIGTERM,也可以通过 KillSignal= 或重启专用的 RestartKillSignal= 改变首个信号。若进程在 TimeoutStopSec= 到期后仍未退出,并且没有关闭 SendSIGKILL=,systemd 会发送 SIGKILL,或发送 FinalKillSignal= 指定的最终信号。

因此,“停止后子进程还在”至少有三种可能:KillMode 只覆盖了主进程;首个信号没有被应用处理;或者服务仍处在停止超时窗口内。不要把尚未到达最终信号的中间状态误认为 KillMode 失效。

[Service]
# 让 systemd 处理整个 unit 控制组,避免 worker 脱离服务生命周期。
KillMode=control-group
# 先给应用优雅退出的机会,时间到后再进入最终信号阶段。
TimeoutStopSec=30s
# 首个停止信号保持默认的 SIGTERM,应用可以在退出钩子中清理资源。
KillSignal=SIGTERM

如果应用确实需要生成转储来排查无法退出的原因,可以在充分了解后配置 FinalKillSignal=SIGABRT 等方案;这不是缩小 KillMode 的理由,且应同时考虑 core 文件限制和运维成本。

按服务结构选择配置

选型时先问“谁拥有子进程的生命周期”。如果 worker、脚本或辅助进程属于这个服务,通常希望 unit 停止后整个控制组都结束,使用默认的 control-group 更稳妥。若希望主进程先收到优雅退出信号,而在最终阶段清理整个控制组,可以选择 mixed。

control-group、mixed、process、none 四种 KillMode 的静态进程范围对比图
图2:对比说明图,展示四种 KillMode 的进程覆盖范围与生命周期边界;这是结果示意图,不是运行截图。

process 只杀主进程,子进程可能继续运行;服务管理器又可能把 unit 视为已经停止,这会造成“服务状态正常但端口仍被占用”或旧 worker 继续消耗资源。none 更进一步,停止时不主动杀进程,只运行停止命令,残留进程与控制组可能继续存在。除非有非常明确的外部生命周期管理者,否则不要用这两个值掩盖应用退出问题。

一个更容易维护的选择表如下:

服务形态优先选择判断依据
主进程和 worker 都由 unit 管理control-group停止后不留下同一服务的子进程
主进程要优雅退出,最终还需清理 workermixed首个信号和最终信号的对象不同
子进程由外部 supervisor 接管先确认所有权,再评估是否需要更窄范围不能仅凭“启动方式不同”就改为 process
希望停止后进程继续存活不要默认使用 none先说明资源、端口和重启策略由谁负责

用 systemctl 检查实际控制组

修改 unit 之前,先把配置来源和当前进程信息分开查看。下面的命令只读取 unit 状态和属性,不会替读者执行停止操作:

# 查看最终合并后的 unit 配置,确认 KillMode 来自哪个片段。
systemctl cat demo.service

# 读取主进程和控制组属性,辅助判断服务是否存在多个受管进程。
systemctl show demo.service -p MainPID -p ControlGroup -p KillMode

# 只看当前 unit 的状态摘要,确认是否仍处在 stopping 状态。
systemctl status demo.service --no-pager

如果配置写在 drop-in 文件中,systemctl cat 比直接打开某个猜测路径更可靠,因为它会把主 unit 和附加片段一起展示。改完后可用 systemctl daemon-reload 让 systemd 重新读取配置;如果要观察新的停止行为,再由运维流程决定是否重启服务。

# 重新加载 unit 文件,不等同于停止或重启服务。
systemctl daemon-reload

# 复查生效值,避免只看文件却遗漏 drop-in 覆盖。
systemctl show demo.service -p KillMode -p TimeoutStopUSec -p KillSignal

排查 KillMode 的四个检查点

  1. 先确认 unit 的最终 KillMode,不要只看某个原始模板文件。
  2. 再确认 MainPID 和 ControlGroup,把主进程与 worker 的关系弄清楚。
  3. 检查 TimeoutStopSec、KillSignal、SendSIGKILL 和 FinalKillSignal 是否改变了停止阶段。
  4. 最后确认残留进程的真正所有者;如果是应用内部启动的子进程,优先修复优雅退出,而不是用 process 或 none 放任它存活。

一句话记忆:control-group 关注“整个 unit 的进程边界”,mixed 关注“主进程先优雅、最终全组清理”,process 只处理主进程,none 不主动处理进程。先确定生命周期所有权,再决定是否偏离默认值。

相关问题

KillMode 默认值是什么?

官方文档给出的默认值是 control-group。除非有明确的进程所有权设计,否则通常不需要为了“停止更快”改成 process。

mixed 和 control-group 有什么区别?

mixed 会让首个终止信号只发给主进程,而后续最终信号覆盖控制组;control-group 则按控制组处理剩余进程。前者适合需要区分主进程优雅退出阶段的服务。

为什么服务停止了,子进程还在?

可能是 KillMode=process 或 none,也可能仍在停止超时窗口、首个信号没有被应用处理,或子进程不在该 unit 的控制组。先用 systemctl show 查看实际属性。

改了 KillMode 需要做什么?

先执行 systemctl daemon-reload 重新读取 unit 配置,再在合适的维护窗口观察服务的停止行为。不要把 daemon-reload 当成停止或重启命令。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>