Zygote 机制与启动过程

什么是 Zygote

  Zygote 直译过来是「受精卵」,放在计算机领域也可以称之为「孵化器」。Android 中的系统服务和几乎所有的应用进程都是由这个 Zygote 进程 fork 而来,相较于完全冷启动,这种方式节约了 ART 虚拟机初始化和部分资源加载的时间,大大提升了应用的启动速度

  值得一提的是,正因系统服务和应用进程都来自 Zygote 的副本,那么我们只要注入 zygote 进程,便同时拥有了注入它们的能力,Xposed 框架正是依赖这一原理修改系统服务和应用进程的

Zygote 的启动过程

  系统启动,内核拉起 init 进程,解析 init.rc 脚本,文件中采用 import 语句来引入 zygote 启动脚本,根据 ro.zygote 属性的值,选择不同的启动脚本:

/system/etc/init/hw/init.rc
1
2
3
4
5
6
7
8
9
10
11
12
# Copyright (C) 2012 The Android Open Source Project
#
# IMPORTANT: Do not create world writable files or directories.
# This is a common source of Android security bugs.
#

......

import /system/etc/init/hw/init.${ro.zygote}.rc

......

  现在市面上的手机大多都搭载 64 位 CPU,被各大厂商使用最为广泛的是 64 位为主,32 位为辅的 Zygote 模式,所以我们来看看 init.zygote64_32.rc

/system/etc/init/hw/init.zygote64_32.rc
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
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal

service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
priority -20
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket usap_pool_secondary stream 660 root system
onrestart restart zygote
task_profiles ProcessCapacityHigh MaxPerformance

这部分涉及的 init.rc 语法

  service 表示服务,其命令格式为:

1
2
3
4
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
  • name:服务名

  • pathname:可执行文件路径

  • argument:启动参数

  • option:对服务的约束选项,此处涉及的有:

  • class <name>:为该服务指定一个类别,同一类别的服务必须同时启动和停止

  • priority <priority>:调度服务进程的优先级,范围 -20 到 19,数字越小优先级越高

  • user <username>:执行此服务前切换到指定用户

  • group <groupname> [ <groupname> ]*:执行此服务之前切指定换到指定组

  • socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]

  服务启动时,同步创建一个名为 /dev/socket/<name> 的 Unix 域套接字,并将其文件描述符以环境变量 ANDROID_SOCKET_<name>=<fd_int> 的方式传递给启动的进程

  • onrestart:服务重新启动时执行的命令

  • task_profiles <profile> [ <profile> ]*:略

  • critical [window=<fatal_crash_window_mins>] [target=<fatal_reboot_target>]

  表示这是一项关键服务,如果它在 fatal_crash_window_mins 内或启动完成前退出超过四次,则会重启到 fatal_reboot_target,默认为 bootloaer

  完整的 android init 文档:init/README.md

  执行了 /system/bin/app_process64,并且传入几个参数,注意这里的 --socket-name 参数,并不是给 Zygote 自己监听用的,而是后续传递给 system server 的时候用到的。我们这里先对应找到 app_process 源码文件 app_main.cpp 中的 main 函数

  在这里,首先做了参数处理:-- 或「第一个非 - 开头参数」之前的所有参数都将被传递给虚拟机,之后的第一个参数为「父目录」,在当前版本的 Android 中并未使用,再之后,可以提供一些特殊参数:

  • --zygote:以 Zygote 模式启动

  • --start-system-server:启动 system server

  • --application:启动应用(非 fork 方式,用于 wrap.sh

  • --nice-name:进程名,后续将调用 setArgV0

  若为 Zygote 模式,所有剩余参数都将传递到 ZygoteInit#main(String[]) 中,否则先读取下一个参数作为主类名,然后再将剩余参数传递到这个类的 main 方法:

frameworks/base/cmds/app_process/app_main.cpp
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class AppRuntime : public AndroidRuntime
{
...
}

int main(int argc, char* const argv[])
{
...

AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
argc--;
argv++;

...

int i;
for (i = 0; i < argc; i++) { // 处理虚拟机参数
...

// 忽略不以 '-' 开头的命令
if (argv[i][0] != '-') {
break;
}

// 忽略 '--'
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i;
break;
}

runtime.addOption(strdup(argv[i]));
}

// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;

++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}

Vector<String8> args;
if (!className.isEmpty()) {
...
} else {
// We're in zygote mode.
maybeCreateDalvikCache();

...

// In zygote mode, pass all remaining arguments to the zygote
// main() method.
for (; i < argc; ++i) {
args.add(String8(argv[i]));
}
}

if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}

if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (!className.isEmpty()) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}

  在 Zygote 模式中,最终调用了 runtime.start() 来启动 Zygote 进程的 Java 虚拟机,这个 runtime 是 AppRuntime 的一个实例,AppRuntime 又继承于 AndroidRuntime,所以我们接着看看 AndroidRuntime 的 start 函数:

frameworks/base/core/jni/AndroidRuntime.cpp
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
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
...

/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
}
}

...
}

  其中反射调用了主类的 main 函数,对于 Zygote 来说,就是 com.android.internal.os.ZygoteInit,至此,Zygote 启动正式进入 Java Framework 层。ZygoteInit 的 main 方法比复杂,我们一点一点来看:

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
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
public static void main(String[] argv) {
ZygoteServer zygoteServer = null;

...

Runnable caller;
try {
...

zygoteServer = new ZygoteServer(isPrimaryZygote);

// ^1
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);

// 如果返回 null,则为 Zygote 进程,否则为 system_server 进程
if (r != null) {
r.run();
return;
}
}

...

// ^2
// 该函数在 fork 出子进程后返回,在 zygote 中则永远循环等待
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with fatal exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}

// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}

  首先看注释 ^1 处,如果我们之前启动 app_process 的时候传入了 --start-system-server,那么此处的 startSystemServer 将为 true,进入 system_server 的启动,我们留到后面来分析

  注释 ^2 处调用 ZygoteServer#runSelectLoop(abiList),从函数签名上已经能够得到很多信息,这是一个循环等待 socket 连接的函数,fork 后在子进程返回,zygote 中则无限等待新的连接,如下所示:

frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
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
class ZygoteServer {
/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
* @param abiList list of ABIs supported by this zygote.
*/
Runnable runSelectLoop(String abiList) {
...

while (true) {
...

int pollReturnValue;
try {
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}

if (pollReturnValue == 0) {
...
} else {
...

ZygoteConnection connection = peers.get(pollIndex);
...

// 在这里处理 AMS 发来的命令
final Runnable command = connection.processCommand(this, multipleForksOK);

...

return command;
}
...
}
}
}

  其内部调用 Os.poll(),当有新的连接到来时,读取参数并调用 ZygoteConnection#processCommand(...) 处理命令,具体细节我们将留到应用启动部分再作分析。

  以上就是 Zygote 进程的完整启动过程,不知道各位看完之后有没有感觉到,其实并没有想象中的那么复杂

System Server 启动过程分析

  再来看看 system_server 是如何启动的,回到 ZygoteInit 的注释 ^1 处,调用了本类的 forkSystemServer() 函数,共有三个入参:

  • abiList

  来自 ro.product.cpu.abilist32/64,后续将与 ro.product.cpu.abilist 进行比较,若二者相等,即代表当前 Zygote 支持的 ABI 已经是本设备支持的所有 ABI,则无需再等待 secondary zygote

  • zygoteSocketName

  参数 --socket-name= 指定的一个字符串,后续会根据此名字确定 secondary zygote 的 socket 名称,并等待其启动

  • zygoteServer

  仅为 fork 后关闭 socket 所用

  现在来看看 forkSystemserver 的方法体:

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* Prepare the arguments and forks for the system server process.
*
* @return A {@code Runnable} that provides an entrypoint into system_server code in the child
* process; {@code null} in the parent.
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
...

/* Hardcoded command line to start the system server */
String[] args = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ "1024,1032,1065,3001,3002,3003,3005,3006,3007,3009,3010,3011,3012",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
ZygoteArguments parsedArgs;

int pid;

try {
ZygoteCommandBuffer commandBuffer = new ZygoteCommandBuffer(args);

...

Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);

...

/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids,
parsedArgs.mRuntimeFlags,
null,
parsedArgs.mPermittedCapabilities,
parsedArgs.mEffectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}

/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}

zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}

return null;
}

  启动把硬编码的参数传递给 Zygote.forkSystemServer(...),返回一个 pid,如果为 0,则表示处于 system_server 进程中,此时按需决定是否需要等待 secondary zygote 可用。最后,调用 handleSystemServerProcess,完成最后的处理工作:

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
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
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
// set umask to 0077 so new files and directories will default to owner-only permissions.
Os.umask(S_IRWXG | S_IRWXO); // ^1

if (parsedArgs.mNiceName != null) {
Process.setArgV0(parsedArgs.mNiceName);
}

final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");

...

if (parsedArgs.mInvokeWith != null) {
// 从 RuntimeInit 启动的特殊模式,后续再重点关注
...
} else {
ClassLoader cl = getOrCreateSystemServerClassLoader();
if (cl != null) {
Thread.currentThread().setContextClassLoader(cl);
}

/*
* Pass the remaining arguments to SystemServer.
*/
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, cl);
}

/* should never reach here */
}
关于「umask」
  • umask(2) 为调用进程设置文件创建模式掩码,并返回之前的掩码值:
1
2
#include <sys/stat.h> 
mode_t umask(mode_t mask);

  下面是一些常用的常量:

屏蔽字 含义
S_IRWXU 0700 所有者:读、写、执行
S_IRUSR 0400 所有者:读
S_IWUSR 0200 所有者:写
S_IXUSR 0100 所有者:执行
S_IRWXG 0070 组:读、写、执行
S_IRGRP 0040 组:读
S_IWGRP 0020 组:写
S_IXGRP 0010 组:执行
S_IRWXO 0007 其他(不在组内):读、写、执行
S_IROTH 0004 其他:读
S_IWOTH 0002 其他:写
S_IXOTH 0001 其他:执行

  在进程创建一个新文件或者目录的时候,会使用掩码对创建时指定的权限进行屏蔽,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <sys/fcntl.h>
#include <sys/stat.h>

int main() {
umask(0); // 清除 umake

// 创建 file-1
open("file-1", O_CREAT | O_WRONLY, 0777);

// 创建 file-2
umask(S_IXGRP | S_IWOTH | S_IXOTH);
open("file-2", O_CREAT | O_WRONLY, 0777);

return 0;
}

  分别查看 file-1 和 file-2 的权限,file-1 的权限为:-rwxrwxrwx,就是我们创建时指定的权限;file-2 的权限为:-rwxrw-r--。可以看到虽然我们指定的是 0777,实际上创建出来的却是 0764,就好像上一步 umask 指定的权限被从中「削除」,这就是 umask 的作用。

  回归正题,ZygoteInit.java 中调用了 Os.umask(S_IRWXG | S_IRWXO);,也就是说用户组和其它用户的权限被全部「削除」,默认新创建的文件只能被所有者访问

  剩余参数会被继续传递到 zygoteInit,之后的部分与应用启动走的流程比较相似,我们同样留到后面再分析

应用启动过程

应用 -> AMS

AMS -> Zygote

Zygote fork 创建新进程

RuntimeInit 机制探究

总结

  • Zygote
  • System Server
  • App

- - 未完待续,本篇博客将持续更新 - -