slice 不能防止僵尸进程产生,但能隔离其资源影响;它通过 cgroup 限制 cpu、内存、进程数,并绑定子进程树,配合 killmode=control-group 和父进程 sigchld 处理,才能有效防控失控 fork 导致的僵尸链式堆积。

把微服务父进程放进独立 slice,不能“从根本上限制僵尸进程产生”,但能有效隔离其资源影响——僵尸进程本身不消耗 CPU 或内存,只占一个进程表项;真正需要管控的是父进程失控后持续 fork 出子进程、又不 wait 的行为。关键不是防僵尸,而是防父进程失管导致的连锁失控。
为什么 slice 有用,但不是“治僵尸”的药
systemd 的 slice 是 cgroup 层级的资源容器,它能限制:
• CPU 时间配额(CPUQuota=)
• 内存上限(MemoryMax=)
• 进程数上限(TasksMax=)
• 子进程树归属(所有 fork 出的子进程默认继承父进程所在 slice)
但 slice 不干预进程状态逻辑:父进程不调用 wait(),子进程退出后仍变僵尸;只是这个僵尸进程及其父进程被锁死在指定 slice 内,不会拖垮整机调度或耗尽全局 PID 数(尤其 TasksMax 能硬限住 fork 爆炸)。
配置独立 slice 并绑定服务的实操步骤
1. 创建专用 slice 文件:
sudo nano /etc/systemd/system/myservice.slice
内容如下:
[Unit] Description=Slice for my microservice DefaultDependencies=no [Slice] CPUQuota=20% MemoryMax=512M TasksMax=128
2. 修改对应 service 文件,显式加入 slice:
sudo nano /etc/systemd/system/myservice.service
在 [Service] 段添加:
- Slice=myservice.slice
- KillMode=control-group(确保 stop 时整个 slice 内进程树一并终止)
- Restart=on-failure(避免父进程崩溃后彻底失管)
- RestartSec=3
3. 重载并启用:
sudo systemctl daemon-reload
sudo systemctl start myservice.service
配合 slice 必须做的三件事,否则依然会出僵尸
• 父进程必须处理 SIGCHLD:在代码中注册信号处理器并循环调用 waitpid(-1, &status, WNOHANG),不能依赖 slice 替你回收
• 禁用用户级 fork 爆炸:在 slice 中设 TasksMax=(如上例 128),超出即触发 systemd 杀死最老进程,阻断僵尸链式生成
• 监控而非静默容忍:用 systemd-cgtop -P 查看该 slice 下 Z 状态进程数;长期 >0 就说明父进程有 bug,需修代码,不是调配置
对比其他方案,slice 的真实优势在哪
• 比单纯 kill -SIGCHLD 更可靠:信号可能丢失,slice + TasksMax 是硬性熔断
• 比 KillMode=process 更安全:后者只杀主进程,子进程变孤儿交由 PID1 管理,仍可能堆积僵尸;而 KillMode=control-group 配合 slice,是整棵树清理
• 比全局 /proc/sys/kernel/pid_max 调优更精准:不伤系统其他服务,只约束特定微服务边界











