2. 系统启动流程基础
要定制 init 脚本实现系统快速启动,首先必须把“从按下电源键到出现登录提示符”这条链路彻底看清楚。Linux 系统的启动并不是一步完成的,而是由固件、引导程序、内核、init 进程和用户空间初始化程序层层接力完成的。每一层都有自己的职责、耗时点和可优化空间。只有明确每一层“做了什么、花了多久、能不能裁剪”,后续的 init 定制才有依据。
一、启动链路的五个阶段
从硬件加电到系统可用,主流 x86 平台大致经历五个阶段:固件初始化(BIOS/UEFI)、Bootloader 引导、内核加载与初始化、init 进程启动、用户空间初始化。它们像接力赛一样依次交接控制权,前一阶段为后一阶段准备好运行环境。
二、各阶段核心原理与关键动作
BIOS/UEFI 阶段:传统 BIOS 执行 POST 自检后,按启动顺序读取 MBR 中的引导代码;UEFI 则从 EFI 系统分区加载 .efi 引导程序。这一阶段主要耗时在硬件自检和磁盘探测,通常难以通过软件大幅优化,但可通过关闭不必要的外设自检、启用快速启动(Fast Boot)缩短时间。
Bootloader 阶段:GRUB2 或 U-Boot 负责把内核镜像和 initrd 加载到内存,并向内核传递启动参数。关键参数包括 root=、init=、console=、quiet 等。其中 init= 参数正是我们定制 init 脚本的入口——它告诉内核第一个用户空间程序是谁。
内核加载阶段:内核解压自身、初始化内存管理、探测并加载驱动、挂载根文件系统,最后启动第一个用户空间进程。若使用 initrd/initramfs,内核会先执行其中的 /init 完成临时根切换,再交给真正的根文件系统。
init 进程阶段:内核启动 PID=1 的进程,它读取配置并拉起系统服务。传统 SysV init 按运行级别串行执行 /etc/rc.d/rc*.d 下的脚本;systemd 通过单元依赖并行启动;而定制 init 脚本则可以只做“必须做的事”,跳过一切非必要服务。
用户空间初始化:服务启动完成后,系统进入登录界面或直接运行目标应用。嵌入式设备常在此阶段启动主业务程序,服务器则启动网络、日志、监控等常驻服务。
三、关键启动参数与配置文件清单
| 阶段 | 关键对象 | 作用 | 可优化点 |
|---|---|---|---|
| BIOS/UEFI | POST、启动顺序 | 硬件自检、选择引导设备 | 关闭冗余自检、Fast Boot |
| Bootloader | grub.cfg、init= |
加载内核、传递参数 | 精简菜单超时、指定自定义 init |
| 内核 | vmlinuz、initrd | 驱动初始化、挂载 rootfs | 裁剪驱动、精简 initramfs |
| init | PID 1、rc 脚本 | 启动系统服务 | 替换为定制脚本、并行启动 |
| 用户空间 | 业务进程、登录 | 提供最终功能 | 按需启动、延迟加载 |
四、实操场景:观察真实启动过程
在调试启动优化前,先学会“看见”启动过程。常用手段包括:
# 查看内核启动日志中的时间戳
dmesg | head -50
# 使用 systemd-analyze 查看各阶段耗时
systemd-analyze
systemd-analyze blame
# 查看内核命令行参数
cat /proc/cmdline
# 确认 PID 1 是谁
ps -p 1 -o pid,comm,args
如果 ps -p 1 显示的不是 systemd 而是我们自己的脚本,说明 init= 参数已经生效,定制 init 成功接管了用户空间初始化。这正是本课程后续要反复使用的验证方法。
init=/sbin/myinit 写入内核命令行,让内核跳过默认 init,直接运行精简脚本。这样可以把启动时间从十几秒压缩到一两秒。
五、常见误区与复盘
- 误区一:认为 init 就是 systemd。init 只是“PID 1 进程”的统称,systemd、SysV init、BusyBox init、自研脚本都可以是 init。
- 误区二:忽略 initrd 的耗时。initramfs 解压和驱动加载常被忽视,实际可能占用数秒。
- 误区三:只优化 init 脚本,不裁剪内核。内核探测无用硬件同样拖慢启动,需协同优化。
- 误区四:服务全部并行就一定快。并行启动会争抢 I/O 和 CPU,需按依赖关系合理编排。
init= 参数后若脚本路径错误或缺少可执行权限,内核会 panic 并提示 “No init found”。调试时务必保留一个可回退的引导项。