启动时间分析:从按下电源到系统就绪
做安全启动性能调优,第一件事不是动手改代码,而是搞清楚时间都花在哪了。我见过太多团队上来就对着某个驱动猛优化,结果发现那个阶段只占整个启动时间的 3%——说白了,方向错了。
这一章我们就来聊聊怎么把启动过程拆开、称重、找瓶颈。我个人习惯把这套方法叫做「启动时间解剖学」。
启动流程的阶段划分
一个典型的安全启动流程,可以分成四个大阶段。每个阶段的特征和优化空间都不一样。
| 阶段 | 起始事件 | 结束事件 | 典型耗时 | 优化潜力 |
|---|---|---|---|---|
| BootROM | 上电复位 | 加载并验证第一级 Bootloader | 50-200ms | 低(固化在芯片内) |
| SPL / TPL | BootROM 跳转 | 初始化 DDR、加载 U-Boot | 100-500ms | 中 |
| U-Boot | SPL 跳转 | 加载并验证内核 | 300-1500ms | 高 |
| Kernel | 内核解压 | init 进程启动 | 500-3000ms | 高 |
你想想看,如果连时间花在哪都不知道,优化就是瞎猫碰死耗子。所以第一步,先把这四个阶段的边界搞清楚。
各阶段耗时分析
每个阶段内部还能继续拆。我习惯用「关键路径法」来分析——找到那条最长的依赖链。
BootROM 阶段
这个阶段基本是芯片厂家写死的。我们能做的只有两件事:确认 BootROM 的配置是否正确(比如启动介质选择),以及检查它是否因为安全校验失败而反复重试。嗯,这里要注意,有些芯片的 BootROM 会做多次重试,每次重试都重新读 Flash,时间就这么浪费了。
SPL / TPL 阶段
这个阶段的核心任务是初始化 DDR 和加载 U-Boot。我建议重点关注三个点:
- DDR 初始化时间——有些开发板的 DDR 初始化代码是通用版,没针对具体颗粒做优化,能差出 100ms
- Flash 读取速度——检查是否开启了 DMA、是否用了最优的 SPI 模式
- 安全校验算法——RSA 2048 和 ECDSA P-256 的验签时间能差 3-5 倍
U-Boot 阶段
这是安全启动性能调优的主战场。U-Boot 里做的事情太多了:设备树解析、驱动初始化、文件系统挂载、安全校验、内核加载。我见过最夸张的一个案例,U-Boot 阶段花了 2.8 秒,其中 1.2 秒是在等一个没用的 USB 控制器初始化。
bootm 命令在加载内核前会做两次完整的 FIT 镜像校验——一次在解析阶段,一次在加载阶段。去掉重复校验后,直接省了 300ms。
Kernel 阶段
内核启动的耗时大头通常在驱动初始化和根文件系统挂载。安全启动在这里的影响主要是:内核签名验证、模块签名验证、以及 IMA(Integrity Measurement Architecture)的度量操作。
性能瓶颈定位方法
定位瓶颈,说白了就是「测量-分析-假设-验证」的循环。我常用的工具有这么几个:
1. 硬件计时法
最原始但最可靠的方法。用 GPIO 在关键节点拉高拉低,然后用示波器或者逻辑分析仪量时间。我在 BootROM 阶段就靠这个办法,发现某个芯片的 Flash 读取时序有问题。
// 在 SPL 入口和出口加 GPIO 标记
gpio_set_value(DEBUG_GPIO, 1); // 开始
// ... SPL 初始化代码 ...
gpio_set_value(DEBUG_GPIO, 0); // 结束
2. 打印时间戳法
U-Boot 和内核都支持时间戳打印。U-Boot 里可以用 get_timer(),内核里可以用 printk 配合 initcall_debug。
// U-Boot 中测量某段代码耗时
unsigned long start = get_timer(0);
// ... 要测量的代码 ...
printf("耗时: %lu ms\n", get_timer(start));
3. bootgraph 工具
内核自带的一个神器。把内核启动的 initcall 调用关系和时间画成图。我建议每个做启动优化的工程师都跑一遍这个工具。
# 内核启动参数加 initcall_debug
# 启动后用 dmesg 抓日志
dmesg > boot.log
# 用 bootgraph.pl 生成 SVG 图
perl scripts/bootgraph.pl boot.log > boot.svg
4. ftrace 和 trace-cmd
当需要更细粒度的分析时,ftrace 是首选。它能追踪到每个函数调用的耗时。
# 启动时开启 ftrace
trace-cmd start -p function_graph -g start_kernel
# 启动完成后停止并导出
trace-cmd stop
trace-cmd report > trace_report.txt
启动时间分析框架图
下面这张图是我自己总结的启动时间分析框架。每次做新项目,我都会先按这个框架走一遍。
实战:一个典型的瓶颈定位案例
我去年帮一个客户做启动优化。他们的设备启动要 8 秒,客户要求砍到 3 秒以内。我们按上面的框架走了一遍:
- 阶段划分——先加 GPIO 标记,测出四个阶段的耗时:BootROM 120ms、SPL 680ms、U-Boot 3.2s、Kernel 4.0s
- 锁定目标——U-Boot 和 Kernel 是主要瓶颈
- 拆解 U-Boot——加打印时间戳,发现 U-Boot 里有个 MMC 驱动初始化花了 1.1 秒,安全校验花了 0.9 秒
- 深入分析——MMC 驱动初始化慢是因为它用了 4-bit 模式,改成 8-bit 模式后降到 0.3 秒;安全校验慢是因为用了 RSA 4096,改成 ECDSA P-384 后降到 0.2 秒
- 验证——改完后 U-Boot 阶段从 3.2 秒降到 1.6 秒
你看,整个过程就是「测量-分析-假设-验证」的循环。没有玄学,只有数据。