1、Zygote的前世今生:Zygote进程的起源、在Android系统中的定位、与init进程的关系

1.1 Zygote到底是个什么角色?

聊Android系统启动,Zygote是绕不开的核心。我经常跟团队里的新人说,你搞懂了Zygote,就等于抓住了Android Java世界的命脉。

Zygote这个词,生物学里叫「受精卵」。名字起得很形象——它分裂出所有App进程,就像细胞分裂一样。说白了,Zygote就是Android系统里所有Java进程的「老祖宗」。

你想想看,我们手机上几十个App,每个App都是一个独立的进程。这些进程从哪来的?不是init直接fork的,而是Zygote一手操办的。嗯,这里要注意,Zygote本身也是由init进程启动的。

1.2 Zygote的起源:它从哪冒出来的?

Android系统启动时,内核加载完,第一个用户态进程就是init。init进程读取init.rc脚本,里面定义了一系列要启动的服务。Zygote就是其中之一。

我记得在Android 4.4时代,Zygote的启动脚本还比较简单。到了Android 10以后,启动参数越来越多,但核心逻辑没变过。

具体来说,init.rc里会有类似这样的配置:

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

看到没?init通过fork+execve的方式启动了app_process这个可执行文件,传入了--zygote参数。app_process是AndroidRuntime的入口,它加载了ZygoteInit.java,然后Java世界就正式登场了。

核心要点:Zygote不是凭空产生的,它是init进程按照init.rc脚本的指示,通过fork创建出来的子进程。init是Zygote的「父亲」。

1.3 Zygote在Android系统中的定位

Zygote在系统里的地位,我用一句话概括:它是Java世界的「盘古」

为什么这么说?因为Zygote做了三件大事:

  • 预加载Java核心类库和资源——所有App共享的那部分,比如android.os、android.content这些包,还有主题资源、通用字符串等。Zygote在启动时一次性加载到内存里,后面fork出来的App进程直接继承,不用再加载一遍。
  • 启动SystemServer——SystemServer是Android系统服务的「大本营」,AMS、WMS、PMS这些核心服务都在SystemServer里跑。Zygote fork出SystemServer进程后,SystemServer再启动各种服务。
  • 响应App启动请求——当用户点击桌面图标,AMS会通过Socket向Zygote发送请求,Zygote fork出新的App进程,然后App进程在Zygote的「模板」基础上继续初始化。

我个人习惯把Zygote比作一个「预制菜中央厨房」。它提前把菜洗好切好(预加载类库),客人点菜时直接炒一下就能上桌(fork进程)。如果没有Zygote,每个App启动都要重新加载一遍核心类库,那启动速度简直不敢想。

避坑指南:我曾经在优化App启动速度时,发现Zygote预加载的类库列表是可以定制的。如果你在framework层开发,可以通过修改zygote预加载的类列表来优化系统启动速度。但要注意,加太多东西会导致Zygote启动变慢,影响整个系统的开机时间。

1.4 Zygote与init进程的关系

init和Zygote的关系,说白了就是「父子关系」。但这里有几个细节值得注意:

维度 init进程 Zygote进程
进程类型 Native进程(C++编写) Java进程(运行在ART上)
启动时机 内核启动后第一个用户态进程 init启动后,按init.rc脚本启动
主要职责 解析init.rc、启动关键服务、处理属性系统 预加载Java类库、启动SystemServer、fork App进程
生命周期 贯穿整个系统运行期 贯穿整个系统运行期(如果挂了会被init重启)
通信方式 通过Socket、属性系统与其它进程通信 通过Socket与SystemServer通信

这里有个有意思的点:Zygote如果挂了,init会把它重新拉起来。init.rc里配置了onrestart指令,Zygote重启时还会顺带重启audioserver、cameraserver、media等服务。为什么会这样?因为Zygote挂了意味着所有Java进程都受影响,系统基本处于半瘫痪状态,必须重启相关服务来恢复。

我记得有一次在项目里做压力测试,故意把Zygote进程杀掉,结果系统直接进入了重启循环。嗯,这其实是一种保护机制——Zygote挂了,系统已经无法正常工作了,不如重启来得干脆。

1.5 Zygote启动流程速览

为了让你对Zygote的「前世今生」有个整体认识,我画了个简化的启动流程:

  1. init进程启动:内核加载完,init成为1号进程。
  2. 解析init.rc:init读取并执行init.rc中的命令,其中就包括启动zygote服务。
  3. fork+execve:init fork出子进程,然后execve执行/system/bin/app_process
  4. 进入AndroidRuntime:app_process启动AndroidRuntime,加载ART虚拟机。
  5. 调用ZygoteInit.main():Java世界的大门打开,ZygoteInit开始执行。
  6. 预加载资源:ZygoteInit加载核心类库、主题资源、通用代码。
  7. 启动SystemServer:Zygote fork出SystemServer进程,SystemServer开始启动各种系统服务。
  8. 进入Socket监听:Zygote进入循环,等待AMS发来的App启动请求。

注意事项:Zygote进程运行在root权限下,这是历史遗留问题。从Android 5.0开始,Google尝试降低Zygote的权限,但出于兼容性考虑,至今Zygote仍然以root身份运行。不过它fork出来的App进程会立刻降权到普通应用级别。这一点在做安全审计时要特别留意。

1.6 小结

Zygote的「前世」是init进程fork出来的一个子进程,它的「今生」是Android Java世界的起点。没有Zygote,就没有SystemServer,没有AMS、WMS这些核心服务,更不可能有我们每天使用的App。

下一章我们会深入Zygote的启动代码,看看ZygoteInit.main()里到底发生了什么。到时候我会带着你一行一行地读代码,把Zygote的「底裤」扒干净。

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