Zygote进程为什么可以创建子进程,然后自己还一直存活着。

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

首先简单分析下Zygote进程的启动流程:

init.cpp会去读取init.rc内容,在init.rc可以看到这么一句话:

import /system/etc/init/hw/init.${ro.zygote}.rc 。这个就是通过系统版本加载对应的init.zygote64.rc和init.zygote32.rc两个文件。

然后就会加载类app_main.cpp,在app_main.cpp的main函数里面可以看到去创建了Zygote进程。并且进程名字就叫zygote。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static const char ZYGOTE_NICE_NAME[] = "zygote";
//....省略部分代码
if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);//设置进程名字
}

if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
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创建子进程的方式是通过fork函数来创建,这里我们需要搞懂父进程和子进程的关系。

父进程&子进程

子进程拥有父进程的全部资源包括代码。那你可能要问了,那子进程从代码哪个地方开始执行?答案是。从fork那个位置开始执行,fork之前的代码子进程不会去执行,他只会执行fork这行代码下面的代码。fork会返回一个pid。这个pid=0代表这是一个子进程,pid<0代表子进程创建失败,pid>0代表是父进程。我们就是通过这个pid的值来判断某些功能让谁来做。这里你可能又有问题了,那父进程在fork之前创建的对象比如Student,那在子进程里面,这个在fork之前创建的对象,他的指向地址和父进程是一样的吗?答案告诉你,是一样的。你在子进程打印出的Student对象地址和父进程打印的Student地址是一样的。那我在给你提个问题,那既然一样,我在子进程修改Student.name值,那父进程里面在获取这个name的值,是子进程修改的值吗?答案是:不是的。子进程修改值不会影响到父进程。因为在子进程那个地址是一个虚拟地址,真正指向的物理地址并不是那个。


在我第一次看ZygoteInit.java这个类的时候,我一直不明白为什么zygote进程能够一直存活,

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 (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
if (r != null) {
r.run();
return;
}
}
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();
}
}

if (caller != null) {
caller.run();
}
}

比如创建systemServer进程的时候,当创建完成后,这个runable一定是不为null的。所以就会执行r.run方法。然后执行return。这里都return了。那为什么zygote进程还是活着的,不是应该死掉了吗。

来看下forkSystemServer方法:

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
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
ZygoteArguments parsedArgs;
int pid;
try {
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的时候去创建了Runbale对象,当pid不是0的时候,返回null。现在在回到ZygoteInit.java里面:

1
2
3
4
5
6
7
8
9
public static void main(String[] argv) {
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
if (r != null) {
r.run();
return;
}
}
}

可以看到当是主进程的时候,这个r是等于null的,所以主进程不会走进return。所以主进程这个时候也不会死。同理你可以去看runSelectLoop方法,他里面有个while的死循环,这就让这个zygote进程可以一直活着了。
想搞懂zygote进程不会死的原因,你一定要先搞懂父进程和子进程的原理和执行逻辑。