330-Android启动流程深度解析:从开机到第一个APP

系列导航331-Bootloader | 332-init进程 | 333-Zygote

你有没有想过这个问题:按下电源键的那一刻,到屏幕上出现桌面壁纸,这中间 Android 系统经历了什么?

这是一个长达数秒、跨越硬件和软件的完整链路。任何一个环节出错,系统就无法启动。

本文是 Android 启动系列的第一篇,总览全局,梳理整个启动链路的轮廓。后三篇会深入每个阶段。


Android 启动的整体流程

Android 本质上是一个基于 Linux 内核的操作系统。所以它的启动链路,和标准 Linux 启动有很多相似之处,但又因为移动端的特殊性多了几个独特的环节。

整个链路可以概括为:

Bash
电源键按下
    │
    ▼
┌─────────────┐
│  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 的启动链路:

Bash
BIOS/UEFI → GRUB → Linux Kernel → systemd(PID 1) → multi-user.target

Android 做了哪些改变?

阶段 标准 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)

从完全断电状态开机。所有东西都要重新初始化。

Bash
电源断电 → 芯片 Boot ROM → Bootloader → Kernel → init → Zygote → SystemServer → Launcher

热启动(Warm Boot / Restart)

系统已经运行,但重启某些组件。例如 adb reboot 或系统更新后的重启。

热启动会跳过 Boot ROM(CPU 已经运行),但 Bootloader 通常还会重新加载。

关机待机(Suspend to RAM)

这是移动端最特殊的一种场景——系统进入低功耗待机状态,CPU 和大部分硬件掉电,但 RAM 保持供电以保存数据。

Bash
用户按电源键 → 系统从 SUSPEND 恢复 → 不走完整启动流程
                                          ↓
                              直接恢复到最后一次 checkpoint(kernel freeze)
                                          ↓
                              Zygote 收到 SIGPWR → resume 线程
                                          ↓
                              ActivityManagerService.restore  ← 快速恢复

为什么这个场景重要? 因为用户对手机最直观的体验不是”开机有多快”,而是”点亮屏幕到桌面出现花了多少秒”。待机恢复的体验比冷启动重要得多。


各阶段的耗时分布

根据对 AOSP 代码和设备实测的分析,一个典型冷启动的时间分布大致如下:

Bash
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 阶段设置:

Bash
sys.boot_completed = 1
sys.bootreason = <reason>

为什么需要这个标志?

因为 APP 可能有需求在系统完全启动后才能运行。例如:

  • 某些 JNI 库只能在 boot_completed=1 后才能加载
  • ACTION_BOOT_COMPLETED 广播通知 APP “系统已就绪”

Bash
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),只有明确授权才能访问。

Bash
# 查看当前进程的 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 模式。

Bash
kernel: dm-verity: enabled
kernel: sha256: ...validated...          (每个 block 都验证)

这保证了 Android 系统的完整性——用户拿到的设备从 Boot ROM 到 Launcher 都是厂商签名的。


动手环节

想实际观察 Android 的启动过程?以下是你可以做的事:

1. 查看 Bootloader 日志

不同芯片的 Bootloader 打印信息不同。高通设备可以通过 adb shell dmesg 看到 Bootloader 残留的日志。

Bash
# 看内核日志,找到 boot 时间戳
adb shell dmesg | grep -i "boot"

# 看系统启动属性
adb shell getprop | grep "sys.boot|ro.boot"

2. 分析 init 进程的启动日志

init 是启动链的核心节点。它的日志在 logcat 里可以看到:

Bash
adb logcat -b events | grep "init"
adb logcat -b system | grep "boot"

3. 看 Zygote 进程

Zygote 是所有 APP 的父进程。通过 adb shell ps -A 找到它:

Bash
adb shell ps -A | grep zygote
# u0_a97   12345   987   4567890  120000  com.android.launcher3  zygote64

Wandos 对比

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 进程的诞生

附录:关键系统属性

Bash
# 系统启动状态
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>

作者:公众号「解码日记」,专注于操作系统、虚拟机、内核级技术深度解析。每周三更新。

最后修改: 2024年7月30日

作者

评论

发表评论

您的邮箱地址不会被公开。