systemd默认并行启动WantedBy=multi-user.target的服务,但依赖After=、Before=和Wants=约束;脚本间无依赖时省略[Unit]段的After/Before可实现最大并行度,强制串行则用After=+Wants=配合Type=oneshot与RemainAfterExit=yes。

systemd服务中如何控制脚本并行启动
systemd默认并行启动所有WantedBy=multi-user.target的服务,但“并行”不等于“无序”,它仍受After=、Before=和Wants=约束。如果你希望两个自定义脚本真正同时跑(比如初始化监控探针和日志轮转),关键不是关掉并行,而是避免隐式依赖。
常见错误是给脚本加了After=network.target后,误以为它会等网络完全就绪——其实只等network.target被激活,不保证DNS或网关可用。
- 若脚本间无依赖,直接在各自
.service文件的[Unit]段省略After/Before即可获得最大并行度 - 想强制串行?用
After=myscript1.service+Wants=myscript1.service让myscript2明确等待前者完成 -
Type=oneshot配合RemainAfterExit=yes时,systemd认为该服务“已启动”,后续依赖它的服务才会继续——这是控制执行顺序最可靠的机制
/etc/rc.local里用&并行执行多个脚本
/etc/rc.local本质是串行执行的shell脚本,但你可以在里面用&把命令扔进后台,实现伪并行。不过这里容易踩坑:rc.local本身会在exit 0时退出,如果后台进程没守护好,可能被一并终止。
正确写法必须加wait或重定向+nohup:
#!/bin/bash /usr/local/bin/script_a.sh > /var/log/a.log 2>&1 & /usr/local/bin/script_b.sh > /var/log/b.log 2>&1 & wait # 等待所有子进程结束,防止rc.local提前退出 exit 0
- 漏掉
wait会导致systemd认为rc-local服务已结束,后续依赖它的服务可能启动失败 - 不重定向stdout/stderr,输出会丢失(开机阶段没有TTY)
- 脚本内部如果有
sleep或ping -w等阻塞操作,建议加timeout 30s防卡死
crontab @reboot触发并行执行的陷阱
@reboot确实能启动多个脚本,但它由cron守护进程派发,**不保证执行时机一致**——每个@reboot条目是独立调度的,内核启动完成时间、cron启动时间、脚本读取顺序都会造成毫秒级偏差。对需要严格同步的场景(如双进程热备握手),这不可靠。
- 不要用
@reboot /a.sh & @reboot /b.sh来模拟并行——这是两条独立cron任务,无法协调 - 若必须用cron,统一写成一个wrapper脚本:
@reboot /usr/local/bin/launch-both.sh,再在脚本里用&& wait - 注意cron环境变量极简,
$PATH通常只有/usr/bin:/bin,调用python3必须写全路径/usr/bin/python3
为什么不用&符号直接在终端里跑启动脚本
有人图省事,在/etc/rc.local或service的ExecStart=里写sh /a.sh & sh /b.sh &,结果发现只有一个脚本真执行了——因为&在非交互shell里行为受限,且systemd对ExecStart只认单个主进程PID。
-
ExecStart不支持&语法,写进去会报错Failed at step EXEC spawning - 想并行,必须封装成单独的shell脚本,再在
ExecStart里调用那个脚本 - 用
setsid或nohup绕过session限制?风险高,systemd会失去进程树控制,导致systemctl stop失效
真正难的不是让脚本“同时开始”,而是确保它们在系统上下文里稳定存活——路径、权限、环境变量、信号处理,任何一个环节断掉,所谓的“并行”就只剩下一个孤零零的僵尸进程。











