systemctl stop 会杀死子进程吗?

在日常使用 systemd 管理 Linux 服务时,一个常见但容易踩坑的问题是:当你执行 systemctl stop 时,systemd 会不会把服务 fork 出来的子进程也一并杀死?

答案是:默认情况下,会的。 但背后的机制远比简单的"杀进程"要复杂,而且 systemd 提供了多种方式来控制这一行为。


问题场景

假设你有一个服务 myapp.service,它的主进程启动后会 fork 出多个工作进程:

bash 复制代码
systemctl start myapp.service
# 主进程 PID 1234,fork 出子进程 1235、1236...
systemctl stop myapp.service

此时,1235 和 1236 这些子进程会怎样?是随主进程一起被清理,还是继续存活变成孤儿进程?


核心机制:cgroup(控制组)

systemd 管理进程的核心武器是 Linux 的 cgroup(Control Group) 。当一个服务启动时,systemd 会为其创建一个独立的 cgroup,并将主进程及其所有子进程都放入这个 cgroup 中。

当你执行 systemctl stop 时,systemd 默认向整个 cgroup 发送终止信号。这意味着无论进程是主进程还是 fork 出来的子进程,只要它还在这个 cgroup 里,都会被 systemd 一并处理。

这种设计的好处是:服务停止时不会留下"孤儿进程",确保资源被彻底清理。


控制行为的关键参数:KillMode

如果你需要改变这种"一刀切"的行为,可以在服务的 unit 文件中通过 KillMode 参数进行精确控制。

四种模式详解

模式 行为说明 适用场景
control-group 默认值 。停止服务时,杀死该 cgroup 中的所有进程(主进程 + 所有子进程) 大多数普通服务
process 只杀死主进程,子进程不会被 systemd 主动终止 主进程管理子进程生命周期的服务(如 Docker)
mixed 先向主进程 发送 SIGTERM 让它优雅退出;超时后,向 cgroup 内其余进程发送 SIGKILL 需要主进程先优雅收尾,再强制清理残留
none 不杀死任何进程(systemd 完全不管进程退出) 已废弃/不推荐,容易产生孤儿进程

配置示例

ini 复制代码
# /etc/systemd/system/myapp.service
[Service]
ExecStart=/usr/bin/myapp
KillMode=mixed
TimeoutStopSec=30

实际案例:Docker 的配置策略

Docker 是一个最典型的需要特殊处理的例子。

Docker daemon(dockerd)会启动大量的容器进程。如果 systemd 在停止 Docker 服务时把这些容器进程全部杀死,那么所有运行中的容器都会异常退出,这显然不是用户想要的。

因此,Docker 的官方 systemd 配置中明确设置了:

ini 复制代码
[Service]
KillMode=process

这样,当你执行 systemctl stop docker 时,systemd 只会终止 dockerd 主进程,而不会去碰那些容器进程。容器是否继续运行,由 Docker 自身来管理。


如果你不想让子进程被杀死

除了修改 KillMode,还有几种常见的解决方案:

1. 修改 KillMode 为 process

ini 复制代码
[Service]
KillMode=process

注意:主进程退出后,子进程会变成孤儿进程继续运行,systemd 会认为服务已经停止。你需要确保这些子进程有适当的退出机制,或者后续手动清理。

2. 让子进程脱离当前 cgroup

在启动脚本中使用 setsidnohup,让子进程进入新的 session 和 cgroup:

bash 复制代码
setsid /usr/bin/background-worker

这样 systemd 就追踪不到这些进程,自然不会去终止它们。

3. 使用独立的 systemd 服务

将子进程放到另一个 .service 单元中管理,通过 PartOf=BindsTo= 建立依赖关系。这样每个进程都有 systemd 的独立生命周期管理,更加规范。

ini 复制代码
# worker.service
[Unit]
PartOf=myapp.service

[Service]
ExecStart=/usr/bin/worker

信号与超时流程

默认情况下,systemctl stop 的执行流程如下:

  1. 发送 SIGTERM 给目标进程(优雅终止请求)
  2. 等待 TimeoutStopSec(默认通常为 90 秒,可在 unit 文件中配置)
  3. 超时后发送 SIGKILL 强制终止仍未退出的进程

如果你使用 KillMode=mixed,流程会变成:

  1. SIGTERM → 仅发送给主进程,让它有机会优雅地清理子进程
  2. 等待超时
  3. SIGKILL → 发送给 cgroup 中剩余的进程(主进程之外的进程)

这种模式在主进程需要"临终遗言"来通知子进程退出时非常有用。


总结

问题 答案
systemctl stop 默认会杀子进程吗? ,通过 cgroup 机制一并清理
如何避免? 设置 KillMode=process 或使用 setsid 脱离 cgroup
最佳实践是什么? 大多数服务保持默认 control-group;特殊服务(如 Docker)使用 process;需要优雅退出的考虑 mixed

理解 systemd 的 cgroup 和 KillMode 机制,能帮助你更精确地控制服务的生命周期,避免"想停主进程却误杀子进程"或者"停了服务却留下一堆孤儿进程"的尴尬局面。

相关推荐
小易撩挨踢11 小时前
[特殊字符] Linux 7.1 内核正式发布:距 7.0 仅 9 周,新 CPU/GPU/文件系统全面升级
linux·运维
vortex511 小时前
Linux进程权限继承研究:从setuid()到exec()与system()的行为差异
linux·服务器·系统安全·suid
swordbob12 小时前
3 大 I/O 模型BIO / NIO / AIO
java·linux·spring
小小小花儿12 小时前
服务器上修改个人账户权限
linux·服务器
Coisinier12 小时前
RHCE中shell脚本基础(磁盘剩余空间监控,Web 服务状态检查,curl 访问 Web 服务并返回状态)
linux·运维·服务器·前端·nginx·操作系统
暮云星影13 小时前
全志linux开发屏幕适配(二)`HDMI`驱动适配说明
linux·arm开发·驱动开发
凡人叶枫13 小时前
Effective C++ 条款38:通过复合塑模出 has-a 或 \“根据某物实现出\
linux·开发语言·c++·windows
charlie11451419113 小时前
嵌入式Linux驱动开发——从轮询到中断
linux·开发语言·驱动开发·嵌入式
无限进步_14 小时前
【Linux】系统级文件I/O与文件描述符深度剖析
linux·运维·服务器