330-Android启动流程深度解析:从开机到第一个APP
系列导航:331-Bootloader | 332-init进程 | 333-Zygote
你有没有想过这个问题:按下电源键的那一刻,到屏幕上出现桌面壁纸,这中间 Android 系统经历了什么?
这是一个长达数秒、跨越硬件和软件的完整链路。任何一个环节出错,系统就无法启动。
本文是 Android 启动系列的第一篇,总览全局,梳理整个启动链路的轮廓。后三篇会深入每个阶段。
Android 启动的整体流程
Android 本质上是一个基于 Linux 内核的操作系统。所以它的启动链路,和标准 Linux 启动有很多相似之处,但又因为移动端的特殊性多了几个独特的环节。
整个链路可以概括为:
电源键按下
│
▼
┌─────────────┐
│ Boot ROM │ ← CPU 芯片内部固化,不可修改
└──────┬──────┘
▼
┌─────────────┐
│ Bootloader │ ← 可升级的引导程序(BL1/BL2/LK/ABOOT)
└──────┬──────┘
▼
┌─────────────┐
│ Linux Kernel│ ← 内核解压、初始化
└──────┬──────┘
▼
┌─────────────┐
│ init │ ← PID 1,用户态第一个进程
└──────┬──────┘
▼
┌─────────────┐
│ Zygote │ ← 所有 APP 的孵化器
└──────┬──────┘
▼
┌─────────────┐
│ SystemServer│ ← 核心系统服务(AMS、WMS、PMS...)
└──────┬──────┘
▼
┌─────────────┐
│ Launcher │ ← 桌面 APP(第一个用户可见的进程)
└─────────────┘注意:这里的 Launcher 指的是 SystemServer 启动后,AMS 回调 finishBooting() 之后launcher 才真正显示桌面。不是 boot_completed 就立刻出现桌面。
和标准 Linux 启动的对比
标准 PC Linux 的启动链路:
BIOS/UEFI → GRUB → Linux Kernel → systemd(PID 1) → multi-user.targetAndroid 做了哪些改变?
| 阶段 | 标准 Linux | Android |
|---|---|---|
| 固件 | BIOS/UEFI | Boot ROM(芯片内固化) |
| 引导程序 | GRUB/LILO | Bootloader(可升级) |
| init | systemd/sysvinit | init(Android 定制的 init) |
| 首个用户进程 | getty/ssh | Zygote |
| 服务管理 | systemd unit | AMS + ServiceThread |
核心区别:Android 没有用 systemd,而是用 Zygote 作为应用进程的孵化器——这是 Android 独有的设计,所有 APP 进程都是从 Zygote fork 出来的。
冷启动 vs 热启动
Android 的启动分两种场景:
冷启动(Cold Boot)
从完全断电状态开机。所有东西都要重新初始化。
电源断电 → 芯片 Boot ROM → Bootloader → Kernel → init → Zygote → SystemServer → Launcher热启动(Warm Boot / Restart)
系统已经运行,但重启某些组件。例如 adb reboot 或系统更新后的重启。
热启动会跳过 Boot ROM(CPU 已经运行),但 Bootloader 通常还会重新加载。
关机待机(Suspend to RAM)
这是移动端最特殊的一种场景——系统进入低功耗待机状态,CPU 和大部分硬件掉电,但 RAM 保持供电以保存数据。
用户按电源键 → 系统从 SUSPEND 恢复 → 不走完整启动流程
↓
直接恢复到最后一次 checkpoint(kernel freeze)
↓
Zygote 收到 SIGPWR → resume 线程
↓
ActivityManagerService.restore ← 快速恢复为什么这个场景重要? 因为用户对手机最直观的体验不是”开机有多快”,而是”点亮屏幕到桌面出现花了多少秒”。待机恢复的体验比冷启动重要得多。
各阶段的耗时分布
根据对 AOSP 代码和设备实测的分析,一个典型冷启动的时间分布大致如下:
Bootloader(LK/ABOOT) : ~1-3s ████
Kernel + early init : ~2-5s ██████
init : ~0.5-2s ████
Zygote 预加载 : ~2-4s ████████
SystemServer : ~1-3s ██████
Launcher 首次加载 : ~1-2s ████
─────────────────────────────────
总计(冷启动) : ~8-20s ████████████████████████████████注意:这只是粗略范围。低端设备(512MB RAM)冷启动到 Launcher 可交互可能要 30 秒以上。旗舰机(12GB RAM)可以在 10 秒以内完成。
| 阶段 | 影响因素 | 优化方向 |
|---|---|---|
| Bootloader | 签名验证、DRM 保护 | 并行验证、分阶段加载 |
| Kernel | 驱动探测、DTB 解析 | 预编译驱动、裁剪不需要的模块 |
| init | 属性服务、文件系统挂载 | 延迟加载、异步化 |
| Zygote | 预加载类、APP 进程创建 | 类预加载优化、Zygote 分叉 |
| SystemServer | 服务按依赖串行启动 | 延迟初始化、并发启动 |
关键概念:boot_completed
在整个启动链路中,有一个重要的节点信号叫 boot_completed。它是 Android 特有的——Linux 本身没有这个概念。
boot_completed 是什么?
boot_completed 是一个系统属性(system property),由 init 进程在 late-init 阶段设置:
sys.boot_completed = 1
sys.bootreason = <reason>为什么需要这个标志?
因为 APP 可能有需求在系统完全启动后才能运行。例如:
- 某些 JNI 库只能在
boot_completed=1后才能加载 ACTION_BOOT_COMPLETED广播通知 APP “系统已就绪”
init 进程设置 sys.boot_completed=1
↓
Native 层的 boot event 发出
↓
Java 层的 ActivityManagerService 收到回调
↓
AMS.finishBooting() → 发送 ACTION_BOOT_COMPLETED 广播一个常见的坑:很多 APP 在收到 BOOT_COMPLETED 后立刻启动 Service,但如果 Service 依赖其他未就绪的系统服务(比如 PackageManagerService 还没扫描完 APK),就会导致 ANR 或启动失败。正确做法是在 onReceive() 里 Handler.postDelayed() 延迟 1-2 秒再启动。
Android 启动的特色:DrgaonRB 和 SELinux
和标准 Linux 不同,Android 在 init 阶段有两个特色的安全机制:
1. SELinux(强制访问控制)
Android 从 4.3 开始引入,5.0 开始强制开启。每个进程有安全上下文(security context),只有明确授权才能访问。
# 查看当前进程的 SELinux 上下文
adb shell id -Z
# u:r:shell:si0 permissive
# u:r:platform_app:so0 minikind
# u:r:init:si0 (init 进程)2. lie-detection(dm-verity)
启动时验证 system 和 vendor 分区的完整性。如果被修改,直接拒绝启动或进入 logcat 可见的 warning 模式。
kernel: dm-verity: enabled
kernel: sha256: ...validated... (每个 block 都验证)这保证了 Android 系统的完整性——用户拿到的设备从 Boot ROM 到 Launcher 都是厂商签名的。
动手环节
想实际观察 Android 的启动过程?以下是你可以做的事:
1. 查看 Bootloader 日志
不同芯片的 Bootloader 打印信息不同。高通设备可以通过 adb shell dmesg 看到 Bootloader 残留的日志。
# 看内核日志,找到 boot 时间戳
adb shell dmesg | grep -i "boot"
# 看系统启动属性
adb shell getprop | grep "sys.boot|ro.boot"2. 分析 init 进程的启动日志
init 是启动链的核心节点。它的日志在 logcat 里可以看到:
adb logcat -b events | grep "init"
adb logcat -b system | grep "boot"3. 看 Zygote 进程
Zygote 是所有 APP 的父进程。通过 adb shell ps -A 找到它:
adb shell ps -A | grep zygote
# u0_a97 12345 987 4567890 120000 com.android.launcher3 zygote64Wandos 对比
Wandos 是一个用 C++ 从零实现 Windows Boot 的教学项目。Android 启动和 Windows 启动的对比:
| 阶段 | Android | Windows | Wandos |
|---|---|---|---|
| 固件 | Boot ROM | BIOS/UEFI | BIOS/UEFI 模拟 |
| 引导程序 | Bootloader (LK/ABOOT) | winload.exe | 简单 PE loader |
| 内核 | Linux Kernel | winload.exe → ntoskrnl.exe | 自研内核 |
| 首个用户进程 | init | smss.exe | 自研 init |
| 应用进程 | Zygote | services.exe → APP | – |
| 桌面 | Launcher | explorer.exe | – |
Wandos 的启动链路比 Android 简单得多——它的目标是从教学角度展示 Windows 启动的核心步骤,而不是复刻完整的 Windows。
系列导航
本篇是总览,接下来三篇深入每个阶段:
- 331 Android Bootloader:Boot ROM → Bootloader → Kernel 的完整链路,高通 LK、ABOOT 详解,签名验证原理
- 332 Android init 进程:init 的配置语言(rc 文件)、服务启动、属性服务、SELinux 安全策略加载
- 333 Android Zygote:Zygote 的预加载机制、fork 模型、与 SystemServer 的关系、APP 进程的诞生
附录:关键系统属性
# 系统启动状态
sys.boot_completed=1
sys.bootreason=<reason>
# 启动阶段
ro.bootmode=<normal|recovery|wipe>
ro.build.display.id=<build>
# 内核信息
ro.kernel.qemu=0
ro.product.cpu.abi=<arm64-v8a|armeabi-v7a>
# 启动时间戳
ro.boottime.<service>=<milliseconds since boot>作者:公众号「解码日记」,专注于操作系统、虚拟机、内核级技术深度解析。每周三更新。
评论