2. 系统启动流程基础

要定制 init 脚本实现系统快速启动,首先必须把“从按下电源键到出现登录提示符”这条链路彻底看清楚。Linux 系统的启动并不是一步完成的,而是由固件、引导程序、内核、init 进程和用户空间初始化程序层层接力完成的。每一层都有自己的职责、耗时点和可优化空间。只有明确每一层“做了什么、花了多久、能不能裁剪”,后续的 init 定制才有依据。

一、启动链路的五个阶段

从硬件加电到系统可用,主流 x86 平台大致经历五个阶段:固件初始化(BIOS/UEFI)、Bootloader 引导、内核加载与初始化、init 进程启动、用户空间初始化。它们像接力赛一样依次交接控制权,前一阶段为后一阶段准备好运行环境。

Linux 系统启动五阶段接力流程 ① BIOS / UEFI POST 自检 查找引导设备 ② Bootloader GRUB / U-Boot 加载内核与 initrd ③ 内核加载 解压、探测硬件 挂载 rootfs ④ init 进程 PID=1 读取配置启动服务 ⑤ 用户空间 服务/登录 系统可用 控制权逐级交接:固件 → 引导程序 → 内核 → init → 用户空间 典型耗时分布(示意) 固件 1~5s Bootloader 1~3s 内核+initrd 2~8s init 服务 3~15s 用户空间 2~10s 优化重点:内核裁剪、initrd 精简、init 脚本并行化、服务按需启动 定制 init 脚本主要作用于第 ④ 阶段,可显著压缩“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”。调试时务必保留一个可回退的引导项。