5.0 KiB
了解 Zygote 的作用
- 启动 SystemServer
- 孵化应用进程
熟悉 Zygote 的启动流程
启动三段式:
进程启动 -> 准备工作 -> LOOP
Zygote 的启动可以分为两块:
- 进程是怎么启动的?
- 进程启动之后做了什么?
第一个问题:进程是怎么启动的?
Init 进程是 Linux 启动之后,用户空间的第一个进程。加载了一个 init.rc 配置文件,里面配置了很多系统服务。通过 fork+execev 系统调用。
// Service Name: zygote, 可执行路径 + 参数
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks
启动进程有两种方式:
第一种是 fork + handle:
pid_t pid = fork();
if(pid == 0){
// child process
} else {
// parent process
}
第二种是 fork + execve:
pid_t pid = fork();
if(pid == 0){
// child process
execve(path, argv, env);
} else {
// parent process
}
调用 fork 函数创建子进程,它会返回两次,子进程返回的 pid == 0,父进程返回的 pid 等于子进程的 pid。
默认情况下,子进程继承了父进程创建的所有资源,如果调用了系统调用 execve 去加载另一个二进程程序的话,那么继承父进程的资源就会被清掉。
信号处理,SIGCHLD 信号:
父进程 fork 出了子进程,如果子进程挂了,那么它的父进程就会收到 SIGCHLD 信号,然后就可以重启。
第二个问题:Zygote 进程启动之后做了什么?
- Zygote 的 Native 世界
- Zygote 的 Java 世界
Native 世界其实就是为了进入 Java 世界做准备,有以下三件事:
- 启动虚拟机
- 注册 Android 的 JNI 函数
- 进入 Java 世界
int main(int argc, char *argv[]) {
JavaVM *jvm;
JNIEnv *env;
// 1
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
// 2
jclass clazz = env->FindClass("ZygoteInit");
jmethodID method = env->GetStaticMethodID(clazz, "Main", "(Ljava/lang/String;)V");
env->CallStaticVoidMethod(clazz, method, args);
jvm->DestoryJavaVM();
}
Java 虚拟机在 Zygote 都已经创建好了,应用程序都是由 Zygote 创建而来的,就直接继承了它的虚拟机。
再来看一下 Zygote 的世界,做了以下事情:
- 通过 registerServerSockert 方法来创建一个 Server 端的 Socket,这个 name 为 zygote 的 socket 用于等待 AMS 请求 Zygote 来创建新的应用程序进程
- 预加载资源,Preload Resources,在 Zygote 进程预加载系统资源后,然后通过它孵化出其他的虚拟机进程,进而共享虚拟机内存和框架层资源,这样大幅度提高应用程序的启动和运行速度
- 启动 System Server
- 进入 Loop 循环,执行 runSelectLoop 方法等待消息去创建应用进程
Zygote 处理请求的关键代码:
boolean runOnce() {
// 1. 读取参数列表
String[] args = readArgumentList();
// 2. 启动子进程
int pid = Zygote.forkAndSpecialize();
if(pid == 0){
// 3. 在进程里面干活,其实就是执行了一个 java 类的 main 函数,java 类名就是上面读取的参数列表,参数列表是 AMS 跨进程发过来的,类名其实就是 ActivityThread
// in child
handleChildProc(args, ...);
return true;
}
}
注意细节:
- Zygote 在 fork 的时候要保证是单线程的,为了避免造成死锁和状态不一致等问题
- Zygote 的 IPC 没有采用 Binder,而是本地 Socket
问题:
- 孵化应用进程这种事为什么不交给 SystemServer 来做,而专门设计一个 Zygote ?
我们知道,应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类资源系统等等,这些都是非常耗时的,如果能在 zygote 里就给这些必要的初始化工作做好,子进程在 fork 的时候就能直接共享,那么这样的话效率就会非常高。这个就是 zygote 存在的价值,这一点 SystemServer 是替代不了的,主要是因为 SystemServer 里跑了一堆系统服务,这些是不能继承到应用程序的。而且我们应用程序在启动的时候,内存空间除了必要的资源外,最好是干干净净的,不要继承一些乱七八糟的东西,所以呢,不如给 SystemServer 和应用进程里都要用到的资源抽出来单独放到一个进程里,也就是这个 zygote 进程,然后 zygote 进程在分别孵化出 SystemServer 进程和应用进程。孵化出来之后,SystemServer 进程和应用程序进程就可以各干各事了。
- Zygote 的 IPC 通信机制为什么不采用 Binder?如果采用 Binder 的话会有什么问题呢?