2. Android 启动流程概览

理解 Android 设备的启动链路,是进行 GKI 设备树与启动适配的前提。Android 设备的启动并不是“一按电源键就进系统”,而是由多个阶段逐级接力完成的:每一级都负责初始化一部分硬件、校验下一级镜像、传递启动参数,最终把控制权交给 Linux 内核,再由内核启动 Android 用户空间。整条链路可以概括为 BootROM → Preloader → LK → Kernel → Android 用户空间,其中前三级通常运行在 SoC 内部的 SRAM 或片上存储中,后两级才真正进入外部 DDR 与文件系统。

下面这张图展示了各阶段的执行位置、关键动作与交接关系,是本节的核心结构图:

Android 启动链路:五级接力与关键交接点 BootROM 固化在 SoC 内 校验并加载 Preloader Preloader 初始化 DDR / 时钟 加载 LK 到内存 LK / U-Boot 显示、Fastboot 加载 boot.img / dtb Linux Kernel 解析 DTB、驱动探测 挂载根文件系统 Android init → Zygote SystemServer 交接 交接 交接 交接 关键阶段与关注点 BootROM 只读固化代码,验证 Preloader 签名,失败则进入 EDL / 下载模式 Preloader 初始化 DDR、时钟、PMIC,把 LK 从 eMMC/UFS 搬到 DRAM LK / U-Boot 提供 Fastboot、显示 Logo,选择 slot,加载 boot.img 与 dtb/dtbo Kernel 解压并解析 DTB,匹配 compatible,probe 驱动,挂载 system/vendor Android 用户空间 init 解析 rc、启动 Zygote、SystemServer,最终进入 Launcher GKI 适配关注点 • boot.img 与 dtb 分离 • dtbo 分区叠加 • vendor_boot 携带 DTB • bootconfig 传参 • A/B slot 选择 • AVB 校验链 • 内核与模块版本匹配

一、核心原理:为什么启动要分这么多级

SoC 上电后,CPU 只能访问一小块片内 SRAM,外部 DDR 尚未初始化,因此不可能直接运行体积庞大的 Linux 内核。启动分级本质上是一个“由小到大、由固化到可变”的引导过程:BootROM 是芯片出厂即固化的最小可信根,它只做最少的事——校验并加载 Preloader;Preloader 负责把 DDR 训练起来,让系统拥有大容量内存;LK(Little Kernel,很多平台也用 U-Boot)则具备显示、Fastboot、分区管理等较完整能力,负责选择启动槽位并加载内核镜像;内核接管后完成驱动初始化与根文件系统挂载;最后由 Android 的 init 拉起用户空间服务。

每一级都遵循“校验下一级、加载下一级、跳转下一级”的模式,这也是 Android Verified Boot(AVB)信任链的基础。任何一级校验失败,启动就会中断或回退到备用槽位。

二、方法流程:各阶段的关键动作

  • BootROM:上电复位后从固定地址执行,读取启动引脚或 eFuse 决定从 eMMC/UFS/SD 启动,验证 Preloader 签名后跳转。
  • Preloader:初始化 DDR、时钟、PMIC、串口,从分区表读取 LK 镜像并搬入 DRAM,然后跳转。
  • LK / U-Boot:初始化显示与 USB,进入 Fastboot 或正常启动;读取 misc 分区决定 slot,加载 boot.img、vendor_boot.img、dtbo。
  • Kernel:自解压后解析 DTB,匹配 compatible 字符串,probe 各驱动,挂载 system、vendor、product 等分区。
  • Android 用户空间:init 读取 init.rc,启动 servicemanager、Zygote、SystemServer,最终显示 Launcher。

在调试时,串口日志是观察这条链路最直接的手段,典型日志片段如下:

[BOOTROM] jump to preloader
[Preloader] DRAM init done, load lk @ 0x4A000000
[LK] boot mode: normal, slot: _a
[LK] loading boot.img ... OK
[Kernel] Booting Linux on physical CPU 0x0
[Kernel] Machine model: MyVendor MyBoard
[Kernel] OF: fdt: dtb overlay applied
[init] init first stage started
[init] starting service 'zygote'

三、案例场景:一次典型的启动适配排查

假设某 GKI 设备在替换内核后卡在开机 Logo,串口停在 [LK] loading boot.img ... OK 之后没有任何内核输出。这说明 LK 已成功加载镜像,但内核没有跑起来。常见原因有三类:一是 boot.img 的 header 版本与 LK 预期不符;二是 DTB 未正确附加或 dtbo 未叠加,导致内核在早期无法识别内存与串口;三是内核与 vendor 模块版本不匹配,导致 init 阶段挂载失败。排查顺序应遵循“先看串口、再看分区、最后看配置”的原则。

调试启动问题时,优先确认串口是否输出、输出停在哪一级。停在 BootROM 多为镜像签名或启动介质问题;停在 Preloader 多为 DDR 配置;停在 LK 多为分区或镜像格式;进入内核后失败则重点看 DTB 与驱动匹配。

四、误区与复盘

很多初学者会把“内核启动”和“Android 启动”混为一谈,认为看到内核 Logo 就算启动成功。实际上,内核只是提供了硬件与文件系统基础,Android 用户空间的 init、Zygote、SystemServer 才是应用可见的部分。另一个常见误区是忽略 dtbo 与 vendor_boot 的作用,在 GKI 架构下,设备树往往不再全部塞进 boot.img,而是拆分到 vendor_boot 与 dtbo 分区,适配时必须同时关注。

阶段 运行位置 关键产物 失败典型现象
BootROM SoC 片内 ROM Preloader 镜像 无串口输出,进入 EDL
Preloader 片内 SRAM DDR 初始化参数 串口停在 DRAM init
LK / U-Boot DRAM boot.img、dtbo 卡在 Logo,Fastboot 可用
Kernel DRAM DTB、驱动模块 内核 panic 或无法挂载根分区
Android 用户空间 DRAM + 存储 init.rc、系统服务 反复重启,停在开机动画
在 GKI 设备上修改设备树时,务必确认修改的是 dtbo 还是 vendor_boot 中的 DTB,并重新打包对应分区。直接改 boot.img 中的 DTB 在部分平台上不会生效,反而会造成启动异常。