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

在启动脚本中使用 setsid 或 nohup,让子进程进入新的 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 机制,能帮助你更精确地控制服务的生命周期,避免"想停主进程却误杀子进程"或者"停了服务却留下一堆孤儿进程"的尴尬局面。

相关推荐
律宏阔10 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔10 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴10 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千10 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei3710 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei3710 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei3710 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW10 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW10 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux
yunwei3711 小时前
eBPF 开发者教程: 简单的 XDP 负载均衡器
linux·后端·性能优化