3、Zygote进程与System Server:Android进程孵化机制,System Server启动流程与核心服务

说实话,Zygote 和 System Server 这两兄弟,是 Android 系统启动过程中最核心的两个角色。我当年刚接触 Android 底层时,总觉得 Zygote 就是个“进程复制机”,System Server 就是个“服务大管家”。后来踩了不少坑才明白——它们之间的关系远比想象中复杂。

今天我们就来彻底搞懂这两个进程。我会结合我实际项目中的一些经历,帮你避开那些容易翻车的点。

3.1 Zygote 进程:Android 的“进程孵化器”

Zygote 这个词,直译是“受精卵”。名字起得很形象——它负责“孕育”出 Android 世界里所有的应用进程。

Zygote 进程本身是由 init 进程启动的。init 解析 init.rc 脚本时,会找到这样一段配置:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    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

注意看 --start-system-server 这个参数。它告诉 Zygote:启动后,顺便把 System Server 也给我拉起来。这就是两个进程的第一次“握手”。

3.2 Zygote 启动流程:从 native 到 Java

Zygote 的启动过程,说白了就是一次从 C++ 世界跨入 Java 世界的过程。我把它拆成几个关键步骤:

  1. init 进程 fork 出 Zygote——此时还是 native 进程
  2. Zygote 执行 app_process 入口——调用 AndroidRuntime::start()
  3. 启动 Java 虚拟机——创建 Dalvik/ART 实例
  4. 注册 JNI 函数——让 Java 代码能调用 native 方法
  5. 调用 ZygoteInit.main()——正式进入 Java 世界
  6. 预加载资源——加载 framework 类、主题、字体等
  7. 启动 System Server——fork 出第二个关键进程
  8. 进入 Socket 监听循环——等待 AMS 发来创建应用的请求

嗯,这里有个细节很多人会忽略:预加载。Zygote 在启动时会加载大量的 Java 类和资源。为什么要这么做?

你想想看,如果每个应用启动时都要重新加载一遍 framework 类,那得多慢?Zygote 的做法是:先把这些公共资源加载到自己进程里,然后 fork 子进程时,利用 Linux 的 COW(写时复制)机制,子进程直接共享这些内存。说白了就是“一次加载,全家共享”。

核心要点:Zygote 预加载的类包括 android.* 包下的几乎所有核心类,以及常用的 UI 组件、资源文件。我统计过,大约有 2000+ 个类和 1000+ 个资源文件。这也是为什么 Zygote 启动后内存占用就很大——它是在为所有应用“垫资”。

3.3 System Server 启动:Android 服务的“总开关”

System Server 是 Zygote fork 出来的第一个子进程。它的任务只有一个:启动并管理 Android 所有核心系统服务

我刚开始看 System Server 源码时,被它那长长的启动顺序搞晕了。后来我画了一张图,才理清楚它的脉络:

System Server 启动流程与核心服务 Zygote fork SystemServer Native 初始化:启动Binder线程池 创建 SystemServiceManager 服务管理器 启动三大类服务:引导服务 → 核心服务 → 其他服务 引导服务 Installer, DeviceIdentifiers 核心服务 AMS, WMS, PMS, PowerManager 其他服务 Camera, Bluetooth, Wifi, NFC SystemServer 进入 Looper.loop() 循环

这张图是我自己整理的。你看,System Server 的启动分三个阶段:

  • 引导服务:最基础的服务,比如 Installer(负责安装应用)、DeviceIdentifiers(设备标识)。这些服务启动早,依赖少。
  • 核心服务:AMS(Activity管理)、WMS(窗口管理)、PMS(包管理)、PowerManager(电源管理)。这些是 Android 的“五脏六腑”。
  • 其他服务:Camera、Bluetooth、Wifi 等。它们依赖核心服务,所以放在最后。

避坑指南:我曾经在定制 ROM 时,遇到一个诡异的问题:系统启动后 Wifi 服务总是崩溃。查了两天才发现,是因为我在 PMS 启动之前就尝试注册 Wifi 服务。PMS 还没准备好,Wifi 拿不到包信息,自然就挂了。记住:服务启动顺序不能乱,这是 System Server 最核心的设计原则。

3.4 核心服务详解:AMS、WMS、PMS

这三个服务,是 Android 应用层的“三驾马车”。我分别说一下它们启动时的关键点。

3.4.1 AMS(ActivityManagerService)

AMS 是 System Server 里最复杂的服务,没有之一。它负责管理 Activity 的生命周期、进程调度、Intent 解析等。

AMS 启动时,会做以下几件事:

  1. 创建 ActivityStackSupervisor——管理所有 Activity 栈
  2. 创建 ProcessList——管理进程优先级和 OOM 调整
  3. 调用 startHomeActivityLocked()——启动桌面 Launcher
  4. 注册 ProcessDeathListener——监听应用进程死亡

我个人觉得,AMS 最巧妙的设计是 进程优先级管理。它把进程分成多个等级:前台进程、可见进程、服务进程、后台进程、空进程。每个等级对应不同的 oom_adj 值。当系统内存不足时,LMK(Low Memory Killer)会根据这个值来杀掉进程。

// 进程优先级定义(部分)
// ProcessList.java
static final int FOREGROUND_APP_ADJ = 0;
static final int VISIBLE_APP_ADJ = 100;
static final int SERVICE_ADJ = 500;
static final int BACKUP_APP_ADJ = 600;
static final int HOME_APP_ADJ = 600;
static final int HIDDEN_APP_MIN_ADJ = 900;
static final int CACHED_APP_MAX_ADJ = 906;

你看,前台进程的 adj 值是 0,几乎不会被杀。而缓存进程的 adj 值是 906,内存紧张时第一个被清理。这个机制保证了用户正在交互的应用始终流畅。

3.4.2 WMS(WindowManagerService)

WMS 负责管理窗口的创建、布局、输入事件分发。它启动时会做:

  • 创建 DisplayContent——管理屏幕显示区域
  • 创建 InputManager——处理触摸、按键事件
  • 创建 WindowAnimator——管理窗口动画

WMS 和 AMS 的交互非常频繁。比如你打开一个 Activity,AMS 会通知 WMS 创建对应的窗口;用户触摸屏幕,WMS 通过 InputManager 拿到事件,再交给 AMS 决定分发给哪个 Activity。

注意:WMS 和 AMS 运行在同一个进程(System Server)中,但它们通过 Binder 通信。为什么不用直接调用?因为 System Server 内部也使用了 Binder 架构,这样设计是为了保持一致性,方便后续扩展和调试。我见过有人试图绕过 Binder 直接调用,结果导致死锁——千万别这么干。

3.4.3 PMS(PackageManagerService)

PMS 负责扫描和安装应用。它启动时最耗时的操作是 扫描 /data/app 和 /system/app 目录

PMS 启动流程:

  1. 扫描系统应用目录(/system/app, /system/priv-app)
  2. 扫描用户应用目录(/data/app)
  3. 解析每个 APK 的 AndroidManifest.xml
  4. 建立包名到组件信息的映射表
  5. 通知 AMS 和 WMS 应用信息已就绪

嗯,这里有个性能问题。如果手机里装了 500 个应用,PMS 启动时扫描 APK 可能要花好几秒。我记得在 Android 8.0 之前,每次开机都要全量扫描,那叫一个慢。后来 Google 引入了 dex2oat 编译缓存包信息数据库,才把启动时间降下来。

3.5 Zygote 与 System Server 的协作关系

这两个进程的协作,可以用一句话概括:Zygote 负责生,System Server 负责养

具体来说:

  • Zygote 启动 System Server 后,就进入 Socket 监听模式
  • System Server 中的 AMS 收到启动应用的请求时,通过 Socket 向 Zygote 发送命令
  • Zygote fork 出新的应用进程,并返回 PID 给 AMS
  • AMS 通过 Binder 通知新进程加载 Application 和 Activity

我画了一个简化的流程图:

Zygote 进程 System Server 应用进程 fork + 启动 Socket 请求创建应用 fork Binder 通信

你看,整个流程就是:Zygote 启动 System Server → System Server 需要创建应用时通知 Zygote → Zygote fork 出应用进程 → System Server 通过 Binder 管理应用。

3.6 常见问题与避坑

最后,分享几个我在实际项目中遇到的问题:

问题 原因 解决方案
System Server 启动超时 某个服务初始化太慢,超过 5 分钟 检查服务依赖,确保没有循环依赖;优化启动逻辑
Zygote fork 失败 内存不足,无法分配 COW 页面 减少 Zygote 预加载的资源;增加 swap 空间
AMS 死锁 多个服务同时持有锁,互相等待 使用 systrace 分析锁竞争;避免在 Binder 调用中持有锁
PMS 扫描过慢 APK 数量多,且没有缓存 启用 dex2oat 缓存;使用增量扫描

个人经验:我曾经遇到一个线上问题:手机开机后,Launcher 要等 30 秒才能显示。用 bootchart 分析后发现,是 PMS 扫描 /data/app 时,有一个损坏的 APK 导致解析卡住。从那以后,我养成了一个习惯:在 PMS 扫描时加一个超时机制,超过 2 秒就跳过这个 APK,等系统启动后再单独处理。

好了,关于 Zygote 和 System Server 的核心机制,就讲到这里。这两个进程是 Android 系统的基石,理解了它们,你就掌握了 Android 启动流程的“命门”。

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