2. 启动流程全景

要优化 Linux 启动性能,第一步不是急着改参数,而是建立一张完整的“启动地图”。只有知道时间花在哪个阶段、哪些阶段可以并行、哪些阶段是串行阻塞点,优化才有方向。Linux 启动从固件到登录界面,大致可划分为五个阶段:固件初始化(BIOS/UEFI)、引导加载程序(GRUB/systemd-boot)、内核加载与初始化、initramfs 早期用户空间、systemd 用户空间启动。每个阶段都有其关键路径和可观测指标。

核心结论:启动总耗时 ≈ 固件时间 + 引导加载时间 + 内核初始化时间 + initramfs 时间 + systemd 用户空间时间。其中 initramfs 和 systemd 用户空间通常是优化收益最大的两个阶段。
Linux 启动五阶段全景与关键路径 ① 固件阶段 BIOS / UEFI POST 硬件自检与枚举 ② 引导加载 GRUB2 / systemd-boot 加载内核与 initramfs ③ 内核初始化 解压、驱动探测 挂载 initramfs ④ initramfs 早期用户空间 加载存储/网络驱动 ⑤ systemd 用户空间服务 并行启动至登录 ~1-5s ~0.5-2s ~0.5-3s ~1-5s ~2-15s 关键路径与可观测点 固件 + 引导加载 • UEFI 快速启动可跳过部分自检 • GRUB timeout 默认 5s,可缩短 • 内核与 initramfs 从磁盘读取 • 观测:dmesg 时间戳、systemd-analyze 内核 + initramfs • 内核解压与驱动探测是串行瓶颈 • initramfs 加载存储驱动后切换根 • 网络启动时 DHCP 可能阻塞 • 观测:rdinit=/bin/sh 调试、initcall_debug systemd 用户空间 • 默认并行启动,依赖关系决定顺序 • default.target 是最终目标 • 关键路径:systemd-analyze critical-chain • 观测:systemd-analyze blame / plot 优化优先级建议 1. 先测量:systemd-analyze 给出各阶段耗时,避免盲目优化。 2. 再定位:critical-chain 找出阻塞关键路径的服务或设备。 3. 后优化:缩短 GRUB 超时、精简 initramfs、并行化 systemd 服务、延迟非关键服务。

核心原理:五个阶段各自在做什么

固件阶段(BIOS/UEFI):上电后 CPU 从复位向量开始执行固件代码。传统 BIOS 进行 POST 自检、枚举 PCI 设备、初始化内存控制器,然后按启动顺序读取 MBR。UEFI 则从 ESP 分区的 EFI 应用程序启动,支持并行初始化和快速启动,通常比 BIOS 快 1~3 秒。此阶段耗时主要取决于硬件自检和固件设置。

引导加载阶段:GRUB2 或 systemd-boot 读取配置文件,显示菜单(默认等待 5 秒),然后将内核镜像(vmlinuz)和 initramfs 加载到内存。GRUB 的 timeout、默认启动项、是否启用图形菜单都会影响时间。此阶段的关键优化点是缩短超时和减少不必要的模块加载。

内核初始化阶段:内核解压自身、初始化内存管理、调度器、中断控制器,然后按顺序执行各子系统的 initcall。驱动探测(尤其是存储、网络、显卡)可能耗时较长。内核最后挂载 initramfs 并执行其中的 /init 脚本。

initramfs 阶段:这是一个临时的根文件系统,包含加载真实根文件系统所需的驱动和工具。它负责加载存储控制器驱动、组装 RAID/LVM、解密 LUKS、配置网络(网络启动场景),然后 switch_root 切换到真实根。此阶段若驱动缺失或等待设备超时,会造成显著延迟。

systemd 用户空间阶段:systemd 作为 PID 1 启动,读取 unit 文件,根据依赖关系并行启动服务。default.target 是最终目标,通常指向 graphical.target 或 multi-user.target。systemd 的并行能力和依赖解析是启动速度的关键,但错误的服务依赖或超时设置会拖慢整体。

方法流程:如何观测各阶段耗时

Linux 提供了多个工具来量化启动各阶段。最常用的是 systemd-analyze 系列命令:

# 查看总体启动时间
systemd-analyze

# 查看各服务耗时排名
systemd-analyze blame

# 查看关键路径(阻塞链)
systemd-analyze critical-chain

# 生成 SVG 时序图
systemd-analyze plot > boot.svg

# 查看固件和引导加载时间
systemd-analyze time

对于内核和 initramfs 阶段,可以通过 dmesg 时间戳观察:

# 查看内核初始化各阶段时间戳
dmesg | grep -E "initcall|Freeing|Run"

# 启用 initcall 调试
# 在内核命令行添加 initcall_debug
技巧:在 GRUB 内核命令行添加 initcall_debug 和 ignore_loglevel,可以打印每个 initcall 的耗时,快速定位内核阶段慢驱动。

案例场景:一次典型的启动延迟排查

某服务器启动耗时 45 秒,用户反馈“开机太慢”。使用 systemd-analyze 得到:

$ systemd-analyze
Startup finished in 3.2s (firmware) + 2.1s (loader) + 4.5s (kernel) + 8.3s (initrd) + 26.9s (userspace) = 45.0s

$ systemd-analyze critical-chain
graphical.target @26.8s
└─multi-user.target @26.8s
  └─docker.service @18.5s + 8.3s
    └─network-online.target @18.4s
      └─NetworkManager-wait-online.service @12.1s + 6.3s
        └─NetworkManager.service @11.8s + 0.3s
          └─dbus.service @11.5s
            └─basic.target @11.4s
              └─sockets.target @11.4s
                └─dbus.socket @11.4s
                  └─sysinit.target @11.3s
                    └─systemd-udev-settle.service @8.2s + 3.1s

关键路径显示:systemd-udev-settle.service 耗时 3.1 秒,NetworkManager-wait-online.service 耗时 6.3 秒,docker.service 耗时 8.3 秒。优化措施:禁用 udev-settle(改用 udev 异步)、将 NetworkManager-wait-online 改为可选依赖、延迟 docker 启动。优化后启动时间降至 22 秒。

误区与复盘

常见误区:
  • 只看总时间,不看关键路径。总时间由最长阻塞链决定,优化非关键路径上的服务没有收益。
  • 盲目禁用服务。禁用依赖服务可能导致功能异常,应先分析依赖关系。
  • 忽略 initramfs。initramfs 阶段常被忽视,但驱动加载和根切换可能耗时数秒。
  • 不区分冷启动和热启动。热启动时固件和引导加载可能更快,但用户空间服务仍需完整启动。

复盘时建议建立基线:在未优化前记录 systemd-analyze 输出、dmesg 时间戳、关键服务耗时。每次变更后对比基线,确认优化有效且未引入回归。对于生产环境,还应考虑启动时间的稳定性,避免因网络或存储波动导致启动时间大幅波动。

阶段划分与关键指标清单

阶段 主要任务 典型耗时 关键观测工具 优化方向
固件 POST、硬件枚举、启动设备选择 1~5s 固件设置、systemd-analyze time 启用 UEFI 快速启动、关闭不必要自检
引导加载 加载内核与 initramfs 0.5~2s GRUB timeout、systemd-analyze time 缩短 timeout、精简菜单、使用 systemd-boot
内核初始化 解压、驱动探测、挂载 initramfs 0.5~3s dmesg、initcall_debug 裁剪内核、内置关键驱动、减少模块
initramfs 加载存储/网络驱动、切换根 1~5s rdinit=/bin/sh、dmesg 精简 initramfs、使用 dracut 优化、避免网络等待
systemd 用户空间 并行启动服务至 default.target 2~15s systemd-analyze blame/critical-chain 并行化、延迟非关键服务、修复依赖超时