系统启动流程(Boot Sequence)

Android 系统启动,说白了就是一部「从按下电源键到看到桌面」的连续剧。我做了这么多年 Framework,每次调试启动问题,都会重新走一遍这个流程。它就像人的心跳,一旦哪个环节断了,整个系统就起不来。

今天我们就来拆解这条链路:BootROM → BootLoader → Kernel → init → Zygote → SystemServer → Launcher。每个阶段都有它的使命,也有它的坑。

Android 系统启动流程 BootROM BootLoader Kernel init Zygote System
Server Launcher 固化ROM 引导加载 驱动+内存 用户空间 Java虚拟机 系统服务 桌面 从硬件到软件,从底层到上层 每个阶段都是下一个阶段的「启动器」 ⚠ 关键:任何一个阶段失败,系统都无法正常启动 常见问题:BootLoader损坏、init.rc语法错误、Zygote OOM

1. BootROM — 芯片上的「第一行代码」

按下电源键的那一刻,CPU 其实还是「空白的」。它需要从某个固定地址去取指令。这个地址指向的就是 BootROM——一块固化在芯片内部的只读存储器。

BootROM 做的事情很简单:初始化硬件、验证 BootLoader 的签名、然后把控制权交给它。嗯,这里要注意,BootROM 是只读的,你改不了它。我遇到过一台设备因为 BootROM 版本太老,导致新版的 BootLoader 签名验证失败,整机变砖。这种问题只能换芯片,没别的办法。

核心要点:

  • BootROM 是 SoC 厂商固化好的,不可修改
  • 它负责最基本的硬件初始化(时钟、内存控制器等)
  • 验证 BootLoader 的签名(安全启动流程)
  • 加载 BootLoader 到 SRAM 并跳转执行

2. BootLoader — 系统的「守门员」

BootLoader 是运行在 Kernel 之前的程序。它负责加载 Kernel 镜像到内存,并传递启动参数。常见的 BootLoader 有 U-Boot、LK(Little Kernel)等。

我个人习惯把 BootLoader 分成两个阶段:

  • 第一阶段(Primary BootLoader):在 SRAM 中运行,初始化 DDR 内存,加载第二阶段代码
  • 第二阶段(Secondary BootLoader):在 DDR 中运行,加载 Kernel 和 ramdisk

我曾经调试过一个启动卡死在 BootLoader 的问题,最后发现是分区表被误写了。BootLoader 找不到 boot 分区,就一直循环等待。所以,分区表备份这件事,我建议你们一定要做。

避坑指南:

我曾经遇到过 BootLoader 因为环境变量配置错误,导致 Kernel 启动参数传错了。比如 console=ttyS0 写成了 ttyS1,结果串口日志全丢了。排查了三天才发现是这里的问题。所以,启动参数一定要 double check

3. Kernel — 操作系统的「心脏」

Kernel 启动后,它会做几件关键的事:

  1. 初始化内存管理、进程调度、文件系统等核心子系统
  2. 加载各种硬件驱动(显示、触摸、网络等)
  3. 挂载根文件系统(rootfs)
  4. 启动第一个用户空间进程——init

你想想看,Kernel 其实不关心你是 Android 还是 Linux 桌面版。它只负责把硬件抽象出来,给上层提供统一的接口。Android 对 Kernel 的改动主要是增加了 Binder 驱动、ashmem、wakelock 等特有模块。

我记得有一次,Kernel 启动后 init 进程一直起不来。查了半天,发现是 ramdisk 打包时少了一个关键文件。Kernel 挂载 rootfs 后找不到 init,直接 panic 了。所以,ramdisk 的完整性是启动成功的前提。

⚠ 注意事项:

Kernel 启动阶段如果遇到 panic,通常会打印一条错误信息然后死机。这时候你需要通过串口或 JTAG 来捕获日志。没有日志,排查 Kernel 启动问题基本靠猜。

4. init — 用户空间的「第一个进程」

init 进程的 PID 是 1。它是所有用户空间进程的祖先。init 的主要工作就是解析 init.rc 文件,然后按照里面的规则启动各种服务。

init.rc 是 Android 启动的核心配置文件。它定义了:

  • 需要创建哪些目录和设备节点
  • 需要设置哪些系统属性
  • 需要启动哪些守护进程(如 servicemanager、ueventd)
  • 最后启动 Zygote

我建议你们仔细看看 init.rc 中 Zygote 的启动部分。它通常长这样:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond

注意看 onrestart 那一串。Zygote 一旦重启,它会连带重启一堆服务。我在项目中遇到过 Zygote 频繁 crash,结果整个系统像多米诺骨牌一样全部重启。那场面,真是「牵一发而动全身」。

5. Zygote — Java 世界的「孵化器」

Zygote 是 Android 的核心进程。它启动时会做两件事:

  1. 加载 Java 虚拟机(ART 或 Dalvik)
  2. 预加载常用的 Java 类和资源

为什么要预加载?说白了就是为了「快」。你想想看,如果每个 App 启动都要重新加载一遍 java.lang.String、android.app.Activity 这些基础类,那得慢成什么样。Zygote 把这些都提前加载好,然后 fork 出子进程时,子进程直接继承这些资源。

Zygote 启动后,它会创建一个 Socket 来监听来自 SystemServer 或 App 的请求。当需要启动新 App 时,Zygote 就 fork 出一个子进程,然后在子进程中执行 App 的入口函数。

关键点:

  • Zygote 是 Android 所有 Java 进程的父进程
  • 它预加载了 Framework 层的大部分类和资源
  • App 启动 = Zygote fork + 加载 App 特有代码
  • Zygote 一旦 crash,所有 Java 进程都会死掉

我曾经遇到过一个诡异的问题:Zygote 启动后总是 OOM。查了很久才发现是预加载的类太多了,超出了默认的堆大小。后来调整了 dalvik.vm.heapsize 属性才解决。所以,预加载的资源不是越多越好,要平衡内存和启动速度。

6. SystemServer — 系统的「大管家」

SystemServer 是 Zygote fork 出的第一个子进程。它负责启动 Android 系统的所有核心服务。这些服务包括:

服务名称 功能描述 启动顺序
PowerManagerService 电源管理、休眠唤醒 1
PackageManagerService 应用安装、卸载、权限管理 2
ActivityManagerService 四大组件管理、进程生命周期 3
WindowManagerService 窗口管理、输入事件分发 4
DisplayManagerService 显示设备管理 5
NotificationManagerService 通知管理 6

SystemServer 的启动过程是串行的。也就是说,必须等前一个服务启动完成,才能启动下一个。我见过一个案例,PackageManagerService 因为扫描一个损坏的 APK 卡住了,导致后面的所有服务都启动不了。最后系统卡在开机动画,进不去桌面。

我个人习惯在 SystemServer 的启动日志中加一些关键节点的 Slog.i 输出。这样一旦启动卡住,我能快速定位是哪个服务出了问题。

7. Launcher — 用户看到的「第一眼」

当 SystemServer 启动完所有核心服务后,它会通过 ActivityManagerService 启动 Launcher。Launcher 本质上就是一个普通的 Android App,只不过它被标记为 android.intent.category.HOME

Launcher 启动后,用户就能看到桌面了。这时候系统启动流程才算真正完成。但注意,Launcher 启动不代表所有服务都准备好了。很多后台服务(如网络、蓝牙)可能还在初始化中。

我记得有一次,Launcher 启动后点击图标没反应。查了半天,发现是 ActivityManagerService 的 mFocusedActivity 没有正确设置。原因是 Launcher 启动时,AMS 还在处理上一个 Activity 的销毁。这种时序问题,在启动阶段特别常见。

调试建议:

如果你遇到启动问题,建议按以下顺序排查:

  1. 看 Kernel 日志(dmesg)—— 确认 Kernel 是否正常启动
  2. 看 init 日志(logcat -b main -s init)—— 确认 init.rc 是否执行正确
  3. 看 Zygote 日志(logcat -b main -s Zygote)—— 确认 Zygote 是否正常 fork
  4. 看 SystemServer 日志(logcat -b system)—— 确认服务是否启动成功

总结

Android 的启动流程,说白了就是一条「接力棒」:BootROM 把棒子交给 BootLoader,BootLoader 交给 Kernel,Kernel 交给 init,init 交给 Zygote,Zygote 交给 SystemServer,最后 SystemServer 把 Launcher 请出来。

每个阶段都有它的职责,也有它的脆弱点。我做了这么多年 Framework,最大的体会就是:启动问题往往是最难排查的,因为日志可能还没准备好,系统可能已经挂了。所以,理解每个阶段的细节,提前做好监控和日志,是每个 Android 工程师的必修课。

嗯,关于启动流程,今天就聊到这里。如果你在实际项目中遇到什么奇葩的启动问题,欢迎来公众号找我聊聊。


公众号:蓝海资料掘金营,微信 deep3321