服务重启后,systemctl status 显示 active (exited),端口却没监听。这个状态只说明 Unit 启动流程已结束,不保证后台进程仍在。下面按 PID、cgroup 和业务探针排查。

一、先分清:active (exited) 不是健康检查
active (exited) 常见于 Type=oneshot 配合 RemainAfterExit=yes。命令退出后 Unit 仍保持 active,适合一次性配置任务,却不适合持续监听端口的 Web 服务。
官方文档说明,RemainAfterExit=true 允许进程退出后继续标记 active;Type=simple 则把 ExecStart 进程作为主进程。状态没有撒谎,只是“岗位说明书”写错了。
二、五条命令建立证据链
systemctl show demo.service \
-p ActiveState -p SubState -p MainPID -p ExecMainStatus -p ControlGroup
systemctl cat demo.service
systemctl status demo.service --no-pager -l
journalctl -u demo.service -b --no-pager -n 100
ss -lntp | grep ':8080'若 MainPID=0,说明没有可跟踪的主进程;若 PID 存在,再确认归属:
ps -o pid,ppid,stat,cmd -p "$(systemctl show -p MainPID --value demo.service)"
systemd-cgls "$(systemctl show -p ControlGroup --value demo.service)"最后用 ss 查端口,用 curl -fsS http://127.0.0.1:8080/health 查接口。active 是过程证据,接口成功才是结果。
三、最常见的四类根因
| 现象 | 常见原因 | 处理方向 |
|---|---|---|
| active (exited),MainPID=0 | oneshot + RemainAfterExit | 持续服务改为 simple/exec |
| 启动脚本很快退出 | 脚本把真正进程放到后台 | 去掉 &,让前台进程成为 MainPID |
| Type=forking 后 PID 不准 | PIDFile 缺失、写迟或残留 | 改前台模式,或正确维护 PIDFile |
| 手动启动正常,重启别的服务后消失 | 进程仍属于错误的 cgroup | 交给独立 Unit 启动和托管 |
四、Type 必须匹配程序的启动模型
前台程序优先使用 Type=simple,启动脚本末尾不要加 &。老式 daemon 确实会 fork 时才考虑 Type=forking,并维护可靠的 PIDFile;一次性任务才适合 Type=oneshot。

软件支持 --daemon 和 --foreground 时,通常选后者。systemd 已是监督器,没必要再让程序“隐身”。
五、别忽略 cgroup:daemonize 不等于逃离父服务
fork、setsid 或 daemonize 可以脱离终端,却不会自动脱离 systemd 的 cgroup。从另一个服务里启动的新 daemon 可能仍在父 Unit 名下;父服务 stop 或 restart 时,默认 KillMode=control-group 会处理其中的剩余进程。
PID=$(pgrep -n -f '/usr/local/bin/demo')
cat "/proc/$PID/cgroup"
systemctl status demo.service --no-pager不要随手改成 KillMode=process。官方文档不推荐 process 或 none,因为子进程会逃离生命周期与资源管理。应给程序创建独立 Unit。
六、一份稳妥的持续服务 Unit
[Unit]
Description=Demo HTTP Service
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/demo
EnvironmentFile=-/etc/demo/demo.env
ExecStart=/usr/local/bin/demo --foreground --listen 127.0.0.1:8080
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
KillMode=control-group
[Install]
WantedBy=multi-user.targetEnvironmentFile 前的减号表示文件缺失时不阻止启动。修改后先静态检查,再 reload:
systemd-analyze verify /etc/systemd/system/demo.service
systemctl daemon-reload
systemctl enable --now demo.service
systemctl restart demo.service若通过 systemctl edit demo.service 覆盖已有 ExecStart,应先写一行空的 ExecStart= 清除旧值,再写新命令,否则可能触发“多个 ExecStart”错误。
七、修完仍失败,按分支继续查
- 状态 failed:看
ExecMainCode、ExecMainStatus和 journal,先解决可执行文件、权限、目录或参数错误。 - 进程在但端口不在:检查监听地址、端口冲突、配置文件是否加载;不要先怪防火墙,因为本机都没监听时,放行端口也没用。
- 频繁重启:用
systemctl show demo.service -p NRestarts -p Result判断启动崩溃,避免把Restart=always当万能创可贴。 - 启动慢:检查
TimeoutStartSec,同时确认程序是否具备明确的就绪信号;盲目拉长超时只会让错误晚一点出现。
八、验收清单
systemd-analyze verify无 Unit 语法错误;ActiveState=active,持续服务的MainPID大于 0;- 主进程命令、PPID 与 cgroup 归属符合预期;
- 目标端口已监听,监听地址正确;
- 本机健康接口请求成功;
- 执行一次受控
systemctl restart后再次通过上述检查; - 重启策略没有掩盖持续崩溃,journal 中无循环报错。
active 是 Unit 生命周期判断,不是业务 SLA。把 Type、MainPID、cgroup 和业务探针串起来,才能确认服务真的可用。
🔕 评论已关闭