为何设置Restart=always的systemd简单服务在主进程被杀死后未重启?
为何设置Restart=always的systemd简单服务在主进程被杀死后未重启?
我来帮你拆解这个问题的核心逻辑——你遇到的情况本质是systemd对Type=simple类型服务的进程跟踪规则和预期不一致导致的。
咱们先对应你的场景来看:你用Type=simple启动bash脚本,脚本又fork出sleep子进程。当你杀死bash主进程后,sleep还留在服务的CGroup(控制组)里,这时候systemd会认为服务仍然处于活跃运行状态,自然不会触发Restart=always的重启逻辑。
具体原因拆解
systemd判断服务是否需要重启,核心看的是服务的整体状态,而非单一主进程的状态:
- 对于
Type=simple的服务,systemd启动ExecStart指定的进程后,就会认为服务启动完成。它会监控主进程,但只要服务的CGroup里还有存活的进程,哪怕主进程挂了,systemd也会标记服务为active (running)状态。 - 你设置的
RemainAfterExit=no是指「进程全部退出后服务不保持活跃」,但这里sleep子进程没退出,CGroup里还有活进程,所以这个配置没生效,服务状态没切换到停止/失败,重启条件也就没触发。
解决办法
给你几个实用方案,按需选择:
1. 将服务类型改为Type=exec
修改test.service的[Service]段:
[Service] Type=exec # 替换原来的Type=simple Restart=always RemainAfterExit=no ExecStart=/home/core/test.sh
Type=exec的规则是:systemd只严格跟踪ExecStart启动的那个进程,只要这个进程退出,不管有没有子进程残留,都会判定服务已退出,进而触发Restart=always重启服务。
2. 在脚本里用exec替换sleep进程
修改你的test.sh:
#!/usr/bin/env bash set -e exec sleep 1h # 加上exec,让sleep直接替换bash进程
这样bash启动sleep后会直接退出,sleep变成systemd跟踪的主进程。此时不管是kill sleep(bash已经不存在了),服务都会因为主进程退出而触发重启。
3. 让主进程退出时带走子进程
你也可以在脚本里添加信号陷阱,当bash进程收到终止信号时,自动杀死sleep子进程:
#!/usr/bin/env bash set -e sleep 1h & PID=$! trap "kill $PID" EXIT TERM INT wait $PID
这样当bash被kill时,会先杀掉sleep,CGroup里没有存活进程,服务状态会切换到失败,从而触发重启。
验证效果
不管选哪个方案,修改后记得重新加载配置并重启服务:
systemctl daemon-reload systemctl restart test.service
再杀死主进程,就能看到systemd自动重启服务了。
备注:内容来源于stack exchange,提问作者comphilip
相关产品推荐
相关产品推荐

