3. init系统演进
在定制 init 脚本实现系统快速启动的工程中,理解不同 init 系统的设计哲学与启动模型,是做出正确技术选型的前提。init 是内核启动后创建的第一个用户态进程(PID 1),它负责挂载文件系统、启动服务、管理运行级,并在系统关机时有序终止进程。从 1983 年的 SysV init 到今天的 systemd,init 系统经历了从“串行脚本”到“并行依赖图”再到“事件驱动”的演进。本节将横向对比五种主流 init 系统,帮助你在嵌入式、服务器、桌面等不同场景中选出最合适的方案。
核心原理:五种 init 的启动模型差异
SysV init 采用“运行级 + 串行脚本”模型。系统启动时按 /etc/rcX.d/ 目录下的 Snn 和 Knn 脚本顺序执行,每个脚本必须等待前一个完成。这种模型简单直观,但启动时间随服务数量线性增长,且无法表达服务间的真实依赖关系。
Upstart 由 Ubuntu 开发,采用“事件驱动 + 作业”模型。服务不再按固定顺序启动,而是由事件(如 started network)触发。它支持并行启动和依赖声明,但事件模型复杂,调试困难,最终被 systemd 取代。
systemd 采用“单元 + 依赖图 + 并行激活”模型。每个服务、挂载点、设备、套接字都是单元(unit),通过 Wants、Requires、After、Before 声明依赖。systemd 按依赖图并行启动,并支持套接字激活、按需启动、cgroup 资源控制,是现代 Linux 发行版的事实标准。
OpenRC 是 Gentoo 的 init 系统,采用“依赖树 + 并行”模型。它保留 Shell 脚本风格,但通过 depend 函数声明依赖,支持并行启动。OpenRC 不替代 PID 1,而是与 SysV init 或 BusyBox init 配合,适合喜欢脚本可控性的场景。
BusyBox init 是嵌入式领域的轻量方案,采用“inittab + 串行”模型。配置文件 /etc/inittab 定义 sysinit、respawn、once 等动作,代码量极小,启动逻辑简单,适合资源受限的嵌入式设备。
方法流程:如何根据场景选择 init 系统
选择 init 系统时,建议按以下流程判断:
- 确定硬件资源:若内存小于 64MB、存储小于 128MB,优先 BusyBox init;若资源充足,进入下一步。
- 确定启动时间目标:若要求 5 秒内完成启动,选择 systemd 或 OpenRC 的并行模式;若可接受 15 秒以上,SysV init 也可用。
- 确定依赖复杂度:服务间存在网络、数据库、文件系统等复杂依赖时,systemd 的依赖图最清晰;简单线性依赖可用 OpenRC。
- 确定维护生态:需要长期维护、日志统一、资源隔离时,systemd 的 journald 和 cgroup 是明显优势。
- 确定团队熟悉度:若团队只熟悉 Shell 脚本,OpenRC 或 SysV init 的学习成本最低。
关键结论:没有“最好”的 init,只有“最合适”的 init。嵌入式选 BusyBox,现代服务器选 systemd,Gentoo/Alpine 选 OpenRC,老旧系统保留 SysV init,Upstart 仅用于维护历史系统。
案例场景:三种典型环境的 init 选型
场景一:工业网关(ARM Cortex-A7,256MB RAM,128MB Flash)。启动时间要求 8 秒内,服务包括网络、串口、Modbus、Web 配置。推荐 BusyBox init + 自定义 rcS 脚本,通过 /etc/inittab 定义 ::sysinit:/etc/init.d/rcS,在 rcS 中并行启动网络和串口服务。若需依赖管理,可升级为 OpenRC,但需评估 Flash 占用。
场景二:云服务器(x86_64,8GB RAM,SSD)。要求快速启动、日志集中、服务隔离。推荐 systemd,使用 systemctl enable 管理服务,通过 systemd-analyze blame 分析启动耗时,利用 After=network.target 声明依赖。典型配置如下:
[Unit]
Description=Custom Init Service
After=network.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my-service
Restart=on-failure
MemoryLimit=256M
[Install]
WantedBy=multi-user.target
场景三:Alpine Linux 容器。镜像体积敏感,服务数量少。推荐 OpenRC,它默认集成在 Alpine 中,通过 rc-update add 添加服务,启动脚本位于 /etc/init.d/。OpenRC 的 depend() 函数可声明 need net、after firewall 等依赖,兼顾轻量与可控。
误区与复盘:常见选型错误
误区一:盲目追求 systemd。在 64MB 内存的嵌入式设备上强行运行 systemd,会导致启动缓慢甚至 OOM。systemd 本身依赖 dbus、cgroup、udev 等组件,资源开销不可忽视。
误区二:认为 BusyBox init 无法并行。BusyBox init 本身串行,但可在 rcS 脚本中用 & 后台启动服务,再用 wait 同步,实现有限并行。
误区三:忽略 Upstart 的维护状态。Upstart 已停止活跃开发,新项目不应选用,仅用于维护 Ubuntu 14.04 等历史系统。
误区四:OpenRC 与 SysV init 混淆。OpenRC 是 init 系统的服务管理框架,可运行在 SysV init 或 BusyBox init 之上,并非独立的 PID 1 实现。
五种 init 系统对比清单
| 特性 | SysV init | Upstart | systemd | OpenRC | BusyBox init |
|---|---|---|---|---|---|
| 启动模型 | 运行级+串行 | 事件驱动 | 依赖图+并行 | 依赖树+并行 | inittab+串行 |
| 依赖声明 | 无 | start on/stop on | Wants/Requires/After | depend() | 无 |
| 并行启动 | 不支持 | 支持 | 支持 | 支持 | 不支持 |
| 资源占用 | 低 | 中 | 高 | 中低 | 极低 |
| 典型发行版 | CentOS 6 | Ubuntu 14.04 | CentOS 7+/Ubuntu 16+ | Gentoo/Alpine | 嵌入式/OpenWrt |
| 适用场景 | 老旧服务器 | 历史系统维护 | 现代服务器/桌面 | 轻量可控系统 | 资源受限设备 |