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、Bootloader、Kernel,在 ramdisk / Init 阶段分叉。移植 Recovery 时,重点往往不在内核,而在 ramdisk、fstab、recovery 二进制与分区表。

二、各阶段职责与 Recovery 的关系

下面这张图展示了从 BootROM 到 SystemServer 的主链,以及 Recovery 模式在 Init 阶段的分叉关系。图中同时标出了各阶段的关键产物与判断条件。

BootROM 固化代码 / 上电自检 Bootloader 读 misc / 选镜像 Kernel 解压 / 挂载 ramdisk Init 解析 init.rc Zygote 孵化应用进程 SystemServer 启动系统服务 正常 Android 系统 Launcher / 应用 Recovery Init init.recovery.rc 挂载 /recovery /cache recovery 二进制 recovery_main() 菜单 / 升级 / 清除数据 分叉判断依据 1. misc 分区 bootloader_message 2. 按键组合(音量+ / 电源) 3. reboot recovery 命令 4. BCB 中的 command 字段 加载镜像: 正常 → boot.img Recovery → recovery.img 共享阶段:BootROM → Bootloader → Kernel 分叉阶段:Init(ramdisk 不同)→ Recovery 或 Zygote/SystemServer Recovery 不启动 Zygote 与 SystemServer,直接运行 recovery 二进制

三、方法流程:从按键到 Recovery 菜单

以“音量上 + 电源键进入 Recovery”为例,完整流程如下:

  1. BootROM 上电自检,从固定地址加载 Bootloader 到 SRAM/DRAM。
  2. Bootloader 检测按键组合,读取 misc 分区中的 BCB(Bootloader Control Block)。
  3. 若 BCB 的 command 字段为 boot-recovery,或按键命中,则加载 recovery.img。
  4. Kernel 启动,解压 ramdisk,执行 /init。
  5. Init 读取 init.rc 与 init.recovery.<hardware>.rc,挂载 /recovery、/cache、/data 等分区。
  6. Init 根据 ro.boot.mode=recovery 等属性,启动 recovery 服务,进入 recovery_main()。
  7. 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 参数。

调试建议:在 Bootloader 阶段打开串口日志,确认加载的是 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