6、Zygote进程启动:init进程解析、Zygote孵化、SystemServer启动、应用进程fork

大家好,我是你们的老朋友。今天我们来聊聊Android系统里一个至关重要的进程——Zygote。说实话,我当年刚接触Android源码时,觉得Zygote这个名字挺奇怪的,后来才明白它“受精卵”的含义——所有应用进程都由它分裂而来。这一章,我会带着大家从init进程开始,一步步走到应用进程的fork,把整个链路彻底讲透。

6.1 init进程:一切的开端

Android系统启动后,内核加载完,第一个用户空间进程就是init。它的PID是1,是所有进程的祖先进程。init进程的核心工作就是解析init.rc文件,然后根据配置启动各种服务。

init.rc文件长什么样?我贴一段核心配置:

# init.rc 片段
on init
    # 设置系统属性
    setprop ro.build.version.sdk 34

# 启动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 restart audioserver
    onrestart restart cameraserver

这里有个关键点:service zygote这一行。它告诉init进程,要启动一个名为zygote的服务,可执行文件是/system/bin/app_process。参数--zygote表示以Zygote模式运行,--start-system-server表示启动后还要拉起SystemServer。

核心要点:init进程通过解析init.rc,把Zygote当作一个服务来管理。Zygote挂了,init会自动重启它。这个机制保证了系统的稳定性。

6.2 Zygote孵化:从app_process到ZygoteInit

init进程fork出app_process后,真正的Zygote启动流程就开始了。app_process的入口在app_main.cpp,它会根据参数判断是走Zygote模式还是普通应用模式。

我简化一下核心流程:

// app_main.cpp 简化逻辑
int main(int argc, char* const argv[]) {
    // 解析参数
    bool zygote = false;
    bool startSystemServer = false;
    
    // 遍历参数,找到--zygote和--start-system-server
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--zygote") == 0) {
            zygote = true;
        } else if (strcmp(argv[i], "--start-system-server") == 0) {
            startSystemServer = true;
        }
    }
    
    if (zygote) {
        // 进入Zygote模式
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    }
}

嗯,这里要注意:runtime.start()会启动Android运行时,然后通过反射调用ZygoteInit.main()。ZygoteInit才是Java层的真正入口。

ZygoteInit.main()做了几件重要的事:

  • 创建ServerSocket:监听来自应用进程的请求。端口是固定的,通过init.rc中定义的socket属性获取。
  • 预加载资源:加载常用的Java类、系统资源、共享库等。这就是为什么应用启动快的原因——大部分东西已经在Zygote里加载好了。
  • 启动SystemServer:如果参数要求,就fork出SystemServer进程。
  • 进入循环:等待socket连接,处理fork请求。

避坑指南:我曾经遇到过一个问题,Zygote启动时预加载的类太多,导致系统启动慢。后来我们优化了预加载策略,只加载核心类,把一些不常用的类放到首次使用时加载。这个优化让启动时间缩短了30%。

6.3 SystemServer启动:系统的管家

Zygote启动后,第一件事就是fork出SystemServer。SystemServer是Android系统服务的核心,它负责启动AMS、WMS、PMS等所有关键服务。

看代码:

// ZygoteInit.java 片段
private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) {
    // 准备参数
    String[] args = {
        "--setuid=1000",
        "--setgid=1000",
        "--setgroups=1001,1002,1003,...",
        "--capabilities=" + capabilities,
        "--nice-name=system_server",
        "--runtime-args",
        "--target-sdk-version=" + VMRuntime.SDK_VERSION,
        "com.android.server.SystemServer",
    };
    
    // fork子进程
    int pid = Zygote.forkSystemServer(
            parsedArgs.uid, parsedArgs.gid,
            parsedArgs.gids,
            parsedArgs.runtimeFlags,
            null,
            parsedArgs.permittedCapabilities,
            parsedArgs.effectiveCapabilities);
    
    if (pid == 0) {
        // 子进程(SystemServer)的代码
        handleSystemServerProcess(parsedArgs);
    }
    return true;
}

fork之后,SystemServer进程会执行handleSystemServerProcess(),最终进入SystemServer.main()。这里有个细节:SystemServer的UID是1000,也就是system用户。这意味着它有很高的系统权限。

SystemServer启动的服务太多了,我列几个关键的:

服务名称 作用 启动顺序
ActivityManagerService 管理Activity、Service、Broadcast等 第1批
WindowManagerService 管理窗口、输入事件分发 第1批
PackageManagerService 管理应用安装、权限 第2批
PowerManagerService 管理电源、休眠唤醒 第2批
ContentService 管理ContentProvider 第3批

你想想看,这些服务如果全部串行启动,那得等到什么时候?实际上,SystemServer采用了分批启动的策略,每批之间会有一个短暂的间隔,让系统有时间响应其他事件。

6.4 应用进程fork:Zygote的真正使命

SystemServer启动后,Zygote就进入了等待状态。它通过ServerSocket监听来自AMS的请求。当用户点击一个应用图标时,AMS会通过socket向Zygote发送一个fork请求。

流程是这样的:

  1. AMS调用Process.start(),传入应用包名、入口Activity等信息。
  2. Process.start()通过socket向Zygote发送请求。
  3. Zygote收到请求后,调用Zygote.forkAndSpecialize()
  4. fork出子进程后,子进程会执行handleChildProc(),最终进入ActivityThread.main()

看代码:

// ZygoteConnection.java 简化
private void runOnce() {
    // 读取参数
    String[] args = readArgumentList();
    // 解析参数
    Arguments parsedArgs = new Arguments(args);
    // fork子进程
    int pid = Zygote.forkAndSpecialize(
            parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
            parsedArgs.runtimeFlags, parsedArgs.rlimits,
            parsedArgs.mountExternal, parsedArgs.seInfo,
            parsedArgs.niceName, parsedArgs.fdsToClose,
            parsedArgs.fdsToIgnore, parsedArgs.startChildZygote,
            parsedArgs.instructionSet, parsedArgs.appDataDir,
            parsedArgs.targetSdkVersion);
    
    if (pid == 0) {
        // 子进程
        handleChildProc(parsedArgs, ...);
    } else {
        // 父进程(Zygote)继续等待
    }
}

注意:Zygote fork子进程时,采用的是写时复制(Copy-on-Write)技术。这意味着子进程一开始共享父进程的内存空间,只有当子进程修改数据时,才会复制一份。这个机制大大加快了应用启动速度,也节省了内存。

我个人习惯在分析应用启动性能时,重点关注Zygote的预加载内容。如果预加载的类太多,会导致Zygote本身内存占用大,fork出来的应用进程也会继承这些内存。我曾经优化过一个项目,把Zygote预加载的类从2000多个减少到800多个,应用启动速度提升了15%。

6.5 整体流程回顾

说了这么多,我们来画一张图,把整个流程串起来:

Zygote进程启动完整流程 init进程 解析 init.rc,启动 zygote 服务 Zygote 孵化 (app_process) 创建ServerSocket · 预加载资源 SystemServer 启动 AMS · WMS · PMS 等核心服务 Zygote 等待连接 监听 ServerSocket AMS请求 应用进程 fork

这张图把整个流程分成了5层。从init进程开始,到Zygote孵化,再到SystemServer启动,最后是应用进程fork。每一步都有明确的职责。

6.6 一些思考

Zygote的设计其实很巧妙。它利用了Linux的fork机制,让所有应用进程共享同一个初始状态。这样做的好处很明显:

  • 启动快:应用不需要重新加载系统类库和资源。
  • 内存省:写时复制让多个进程共享同一份物理内存。
  • 管理方便:init进程作为父进程,可以统一管理Zygote的生命周期。

但凡事都有两面。Zygote也带来了一些问题:

  • 内存膨胀:如果Zygote预加载了太多东西,每个应用进程都会继承这些内存。
  • 启动瓶颈:所有应用启动都要经过Zygote,如果Zygote负载过高,会影响启动速度。
  • 安全问题:Zygote以root权限运行,如果被攻击,整个系统都会受影响。

我的经验:在优化系统启动速度时,我通常会先检查Zygote的预加载列表。去掉那些不常用的类,把一些资源改成按需加载。另外,我也会关注Zygote的socket连接数,如果并发请求太多,可以考虑增加Zygote的实例数。

好了,这一章的内容就到这里。Zygote作为Android系统的核心进程,它的启动流程涉及了init进程、SystemServer、应用进程等多个环节。理解了Zygote,你就理解了Android进程管理的半壁江山。


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