Android Zygote 进程启动流程

Android 系统 zygote 进程的启动流程分析。

Android zygote 进程启动过程的要点如下:

  • init 进程启动了 zygote 进程。具体来说,init 进程通过 zygote-start 触发器(trigger)拉起 zygote 服务(service)
  • zygote 进程是系统中的第一个 Java 进程
  • zygote 进程对应的 Java 类是 com.android.internal.os.ZygoteInit
  • zygote 进程启动中有3个关键动作
    • 预加载资源
    • 启动 SystemServer
    • 启动 ZygoteServer

init.rc 中的 zygote 服务

zygote service 的定义见 init.zygote64.rc

1
2
# 第1行
service zygote /system/bin/app_process64 ...

启动 zygote service 的 trigger 和 action 见 init.rc

1
2
3
4
5
6
7
8
9
10
11
12
13
# 第557行
# Mount filesystems and start core system services.
on late-init
...
# Now we can start zygote for devices with file based encryption
trigger zygote-start

# 第1090行
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
...
start zygote
start zygote_secondary
...

关于 service, action, trigger 等概念的详细介绍可以参考 Android init language

app_process64 命令

通过前面的 init.rc 配置我们知道 zygote 进程对应于 /system/bin/app_process64 命令。

app_process64 命令相关代码在 /base/cmds/app_process 目录下。

这是一个非常简单的项目,只有一个不到400行代码的 app_main.cpp。我们重点关注 main 函数,精简后的流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main(int argc, char* const argv[])
{
...
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
bool zygote = false;
bool startSystemServer = false;
...
if (zygote) {
// 分支1
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (!className.isEmpty()) {
// 分支2
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
...
}
}
  • zygote 进程的启动,对应于分支1
  • 普通应用进程的启动,对应于分支2

AppRuntime 及其父类 AndroidRuntime 其实是个简单的 wrapper,对 Java VM 进行一层薄的包装,其定义见这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class AndroidRuntime {

// 启动 Java 虚拟机并执行名为 className 的类中的 main 方法
void AndroidRuntime::start(const char* className);

// 启动 Java 虚拟机
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
// 超级复杂的 VM 参数配置
...
// 简单地通过 JNI 调用创建一个 VM
JNI_CreateJavaVM()
...
}
}

class AppRuntime : public AndroidRuntime {}

ZygoteInit 类

完整的类名是 com.android.internal.os.ZygoteInit,代码见 ZygoteInit.java

ZygoteInit 类及相关类的类图。

ZygoteInit 类的主要操作包括:

  • 预加载资源
  • 启动 SystemServer (如果指定了 --start-system-server 的话)
  • 启动 ZygoteServer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] argv) {
...
# 预加载资源
if (!enableLazyPreload) {
preload(bootTimingsTraceLog);
}
...
# 创建 ZygoteServer
ZygoteServer zygoteServer = new ZygoteServer(isPrimaryZygote);

# 启动 SystemServer
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
if (r != null) {
r.run();
return;
}
}
# 启动 ZygoteServer 消息循环
caller = zygoteServer.runSelectLoop(abiList);
...
}

预加载资源

1
2
3
4
5
6
7
8
9
preload() {
...
preloadClasses();
preloadResources();
...
preloadSharedLibraries();
preloadTextResources();
...
}
  • 预加载常用类
    • /system/etc/preloaded-classes 读取类名
    • 对每个类名调用一次 Class.forName() 方法
  • 预加载常用的 drawable 和 color list 资源
    • 通过 Resources.getSystem() 获取系统级别的 Resources 对象
    • 在该对象上预加载各个 com.android.internal.R.array.preloaded_xxx 对应的资源
  • 预加载动态库 libandroid.solibjnigraphics.so
  • 预加载常用字符资源
    • Hyphenator.init()
    • TextView.preloadFontCache()

启动 ZygoteServer

ZygoteServer 通过 runSelectLoop() 启动消息循环:

1
2
3
4
5
6
7
8
9
10
11
12
Runnable runSelectLoop(String abiList) {
...
while (true) {
...
// Zygote server socket
ZygoteConnection connection = acceptCommandPeer(abiList);
...
final Runnable command =
connection.processCommand(this, multipleForksOK);
...
}
}

ZygoteConnection 是 ZygoteServer 和相应客户端之间的一条连接,两端之间的通信过程发生在 ZygoteConnection.processCommand() 方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
ZygoteArguments parsedArgs = ZygoteArguments.getInstance(
new ZygoteCommandBuffer(mSocket));
...
pid = Zygote.forkAndSpecialize(parsedArgs)
if (pid == 0) {
// in child
return handleChildProc(parsedArgs, ...);
} else {
// In the parent
handleParentProc(pid, serverPipeFd);
return null;
}
}
  • 第1步,读取客户端请求并解析成 parsedArgs 参数
  • 第2步,从 zygote 进程 fork 出一个新的进程
  • 第3步,初始化新的进程并启动 parseArgs 中指定的 Java 类

当然,这里还有个问题:谁是客户端。这个问题在后续文章中解答。

启动 SystemServer

简单起见,这里我们只要知道通过以下调用关系最终在新的 SystemServer 进程执行 SystemServer.main() 函数即可,具体的分析放在下一篇中。

1
2
3
4
5
6
ZygoteInit.main()
forkSystemServer()
Zygote.forkSystemServer()
handleSystemServerProcess()
ZygoteInit.zygoteInit()
-> SystemServer.main()

上述调用关系对应的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public static void main(String[] argv) {
boolean startSystemServer = false;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
}
...
}
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
...
}
}

private static Runnable forkSystemServer(String abiList, String socketName,...) {
/* Hardcoded command line to start the system server */
String[] args = {
...
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};

...
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(...);
...
/* For child process */
if (pid == 0) {
...
zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}
}

private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
...
/*
* Pass the remaining arguments to SystemServer.
*/
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, cl);

}

更正

今天(7月20)打日志观察进程启动流程:

  • 我确认自己对 init、zygote 和 system_server 三个进程的启动流程理解大体正确
  • 但也有不正确的地方。我偶然通过日志发现普通 app 进程启动时似乎并不走前面”启动 ZygoteServer”那一节列出的任何代码逻辑

今天又看了一下 runSelectLoop() 里的逻辑,比预期的要复杂一些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Runnable runSelectLoop(String abiList) {
...
while (true) {
...
// Zygote server socket
ZygoteConnection connection = acceptCommandPeer(abiList);
...
if (...) {
final Runnable command =
connection.processCommand(this, multipleForksOK);
} else {
// fillUsapPool() 最终调用 Zygote.forkUsap()
final Runnable command =
fillUsapPool(sessionSocketRawFDs, isPriorityRefill);
}
...
}
}

processCommand() 里面 fork 进程,也有新老不同的两种处理方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
ZygoteArguments parsedArgs = ZygoteArguments.getInstance(
new ZygoteCommandBuffer(mSocket));
...
if () {
pid = Zygote.forkAndSpecialize(parsedArgs)
if (pid == 0) {
// in child
return handleChildProc(parsedArgs, ...);
} else {
// In the parent
handleParentProc(pid, serverPipeFd);
return null;
}
} else {
Runnable result = Zygote.forkSimpleApps(argBuffer,
zygoteServer.getZygoteSocketFileDescriptor(),
peer.getUid(), Zygote.minChildUid(peer), parsedArgs.mNiceName);
if (result == null) { // parent }
else { // child }
}
}

Zygote 中有4个不同的 forkXXX() 方法:

  • forkSystemServer()
  • forkUsap()
  • forkSimpleApps()
  • forkAndSpecialize()

除第1个 forkSystemServer() 功能明确外,剩下三个待进一步了解。

TODO

粗线条地理解 zygote 进程启动过程并不难,但更好地理解还需要先了解一些背景知识。待整理的背景知识点:

  • Linux 的进程 fork 机制
  • Android 的 Local Socket 机制,这个本质上应该是 UNIX domain socket
  • C++ 代码中如何创建一个 JavaVM

参考