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请求。
流程是这样的:
- AMS调用
Process.start(),传入应用包名、入口Activity等信息。 Process.start()通过socket向Zygote发送请求。- Zygote收到请求后,调用
Zygote.forkAndSpecialize()。 - 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 整体流程回顾
说了这么多,我们来画一张图,把整个流程串起来:
这张图把整个流程分成了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进程管理的半壁江山。