2. Android系统启动流程
理解 Android 的启动流程,是 Recovery 系统开发与移植的基础。Recovery 并不是一个“独立运行的小系统”,它实际上是 Android 启动链条上的一个特殊分支:与正常启动共享 BootROM、Bootloader、Kernel 等前置阶段,但在挂载根文件系统、启动用户空间第一个进程时发生分叉,从而进入 Recovery 而不是正常系统。掌握这条主链与分叉点,才能准确定位移植中“进不去 Recovery”“黑屏”“卡在 logo”等问题。
一、核心原理:一条主链,两个分叉
Android 从上电到可用,依次经历 BootROM、Bootloader、Kernel、Init、Zygote、SystemServer 等阶段。前三个阶段属于“硬件与内核层”,后三个阶段属于“用户空间层”。Recovery 与正常启动的分叉点,就发生在 Kernel 启动用户空间第一个进程(Init)时。
具体来说,Bootloader 会根据 misc 分区中的 bootloader_message 结构、按键组合或 reboot recovery 命令,决定把哪个 boot 镜像加载进内存。正常启动加载 boot.img,Recovery 启动加载 recovery.img(或 A/B 设备上的 recovery ramdisk)。两者内核可以相同,但 ramdisk 内容不同,最终启动的 Init 配置也不同。
二、各阶段职责与 Recovery 的关系
下面这张图展示了从 BootROM 到 SystemServer 的主链,以及 Recovery 模式在 Init 阶段的分叉关系。图中同时标出了各阶段的关键产物与判断条件。
三、方法流程:从按键到 Recovery 菜单
以“音量上 + 电源键进入 Recovery”为例,完整流程如下:
- BootROM 上电自检,从固定地址加载 Bootloader 到 SRAM/DRAM。
- Bootloader 检测按键组合,读取
misc分区中的 BCB(Bootloader Control Block)。 - 若 BCB 的
command字段为boot-recovery,或按键命中,则加载recovery.img。 - Kernel 启动,解压 ramdisk,执行
/init。 - Init 读取
init.rc与init.recovery.<hardware>.rc,挂载/recovery、/cache、/data等分区。 - Init 根据
ro.boot.mode=recovery等属性,启动recovery服务,进入recovery_main()。 - Recovery 显示菜单,等待用户选择“重启系统”“清除数据”“应用升级包”等操作。
正常启动时,Init 会继续启动 servicemanager、zygote,由 Zygote 孵化 SystemServer,最终进入 Launcher。Recovery 则完全跳过 Zygote 与 SystemServer,因此它没有 Java 框架层,只有 C/C++ 的 recovery 二进制和少量 native 服务。
四、案例与场景:移植中的典型问题
在 Recovery 移植中,最常见的问题不是内核,而是 ramdisk 与分区配置。例如某项目正常系统可以启动,但进入 Recovery 后黑屏。排查发现:recovery.img 的 ramdisk 中 init.recovery.rc 引用了不存在的 /dev/block/by-name/recovery,导致挂载失败。修正 fstab 后恢复正常。
另一个典型场景是 A/B 设备。A/B 设备没有独立 recovery 分区,Recovery 作为 boot 分区 ramdisk 的一部分存在。此时 Bootloader 通过 slot 后缀选择 boot_a 或 boot_b,Recovery 与正常系统共享同一个内核,仅 ramdisk 中的 init 逻辑不同。移植时必须同时更新 fstab 中的 slotselect 参数。
boot.img 还是 recovery.img;在 Kernel 阶段查看 dmesg 中 ramdisk 解压与 init 执行情况;在 Init 阶段查看 init.recovery.rc 是否被解析。
五、误区与复盘
| 常见误区 | 正确理解 | 排查手段 |
|---|---|---|
| Recovery 是独立系统,与正常启动无关 | Recovery 共享 BootROM、Bootloader、Kernel,仅在 Init 阶段分叉 | 对比 boot.img 与 recovery.img 的 ramdisk 内容 |
| 进不去 Recovery 一定是内核问题 | 多数是 BCB、按键、fstab 或 ramdisk 配置问题 | 读 misc 分区、查串口日志、检查 init.recovery.rc |
| Recovery 会启动 Zygote 和 SystemServer | Recovery 不启动 Java 框架层,直接运行 recovery 二进制 | 查看进程列表,确认无 zygote/system_server |
| A/B 设备也有独立 recovery 分区 | A/B 设备 Recovery 位于 boot 分区 ramdisk 中 | 检查分区表与 slot 后缀 |
复盘时建议按“BootROM → Bootloader → Kernel → Init → Recovery”逐段确认:BootROM 是否完成自检,Bootloader 是否读到正确 BCB,Kernel 是否成功挂载 ramdisk,Init 是否解析到 recovery 配置,recovery 二进制是否被拉起。每一段都有对应的日志与分区证据,逐段排除即可定位问题。
# 查看 misc 分区中的 BCB 内容(示例)
adb shell dd if=/dev/block/by-name/misc bs=1 count=64 2>/dev/null | hexdump -C
# 查看当前启动模式
adb shell getprop ro.boot.mode
# 输出 recovery 表示当前处于 Recovery 模式
# 查看 recovery 相关日志
adb shell dmesg | grep -i recovery
adb shell logcat -b all | grep -i recovery