respawn 进程守护 让关键进程崩溃自动重启
为什么需要 respawn
嵌入式系统通常没有完整的进程管理框架(没有 systemd、没有 supervisord),但某些关键进程不能因为崩溃就挂掉——比如:
- 网络守护进程(web 管理界面、DHCP 服务器)
- 设备控制主循环
- 日志收集服务
- 远程访问 shell
BusyBox init 提供了最轻量的方案:在 /etc/inittab 里用 respawn action,init 会无限循环地启动你指定的进程——进程退出后立即重新拉起。
基本用法
# /etc/inittab 里加一行
myservice::respawn:/usr/sbin/myservice --option value
这样就完成了。如果 /usr/sbin/myservice 崩溃退出,BusyBox init 会立即重新执行这条命令,把进程拉起来。没有额外的守护进程,没有配置文件复杂度。
respawn 和 once 的区别
# respawn:进程退出后自动重启(无限循环)
a::respawn:/usr/bin/daemon-a
# once:进程只跑一次,退出后不再重启
b::once:/usr/bin/daemon-b
嵌入式场景里 respawn 几乎覆盖了所有需要守护的场景,once 主要用于一次性初始化任务。
避免 respawn 风暴
如果某个进程启动后立刻崩溃(比如配置文件错了、依赖没装),BusyBox init 会疯狂重试——每秒启动几百次,浪费 CPU 和 I/O。BusyBox init 内置了风暴保护:
启动失败频率检测:BusyBox init 记录每个 respawn 进程的启动时间间隔。如果一个进程在 5 秒内连续启动超过 3 次,init 会认为它有问题,打印警告并延迟启动(每次延迟越来越长),而不是立即重试。
这个机制防止了在系统故障时 CPU 被白白烧掉,但也意味着进程一旦有 bug,修复后可能需要重启 init 才能让它立即恢复——给 init 发 HUP 信号重新加载 inittab 可以重置风暴计数器。
askfirst 的安全模式
BusyBox init 有一个独特的 action 叫 askfirst——respawn 但首次启动前会提示按回车确认:
# askfirst:首次启动前要手动按回车确认
# 后续退出/重启就不需要了,自动 respawn
shell::askfirst:/bin/sh
这个设计是为了救援模式——如果系统启动脚本跑挂了,你可以通过串口连上去,按回车进入 shell 修复配置。对于嵌入式开发阶段特别有用。
respawn vs 手动写守护脚本
很多人第一反应是自己写个 Shell 守护循环:
# 反例:别这么干
while true; do
/usr/sbin/myservice
sleep 1
done &
BusyBox init 的 respawn 比这种方式好太多:
| 对比项 | BusyBox respawn | Shell while 循环 |
|---|---|---|
| 守护者身份 | PID 1(init) | 后台 shell 进程 |
| 崩溃时的响应 | 立即重启 | 有 sleep 延迟 |
| 风暴保护 | 内置 | 需要自己写 |
| 配置方式 | inittab 一行 | 额外脚本 |
| 与系统集成 | init 管理 | 独立守护,关机时需要额外 kill |
respawn 没有任何缺点——能就用 respawn,不要自己写守护循环。
实战示例
# /etc/inittab 中守护关键服务
# DHCP 服务——网络配置不能丢
dhcp::respawn:/usr/sbin/udhcpc -i eth0
# Web 管理界面
httpd::respawn:/usr/sbin/httpd -p 80 -h /var/www
# 串口 shell(开发调试用)
ttyS0::respawn:/sbin/getty 115200 ttyS0
# 设备主控制循环
control::respawn:/usr/local/bin/device-controller
# 日志系统
syslog::respawn:/sbin/syslogd -n
klog::respawn:/sbin/klogd -n
修改完 inittab 后给 init 发 HUP 让它重新加载:
busybox kill -HUP 1