2. 启动流程全景
要优化 Linux 启动性能,第一步不是急着改参数,而是建立一张完整的“启动地图”。只有知道时间花在哪个阶段、哪些阶段可以并行、哪些阶段是串行阻塞点,优化才有方向。Linux 启动从固件到登录界面,大致可划分为五个阶段:固件初始化(BIOS/UEFI)、引导加载程序(GRUB/systemd-boot)、内核加载与初始化、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
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 | 并行化、延迟非关键服务、修复依赖超时 |