You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

5.0 KiB

谈谈你对 Zygote 的理解?

了解 Zygote 的作用

  1. 启动 SystemServer
  2. 孵化应用进程

熟悉 Zygote 的启动流程

启动三段式:

进程启动 -> 准备工作 -> LOOP

Zygote 的启动可以分为两块:

  1. 进程是怎么启动的?
  2. 进程启动之后做了什么?

第一个问题:进程是怎么启动的?

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 世界做准备,有以下三件事:

  1. 启动虚拟机
  2. 注册 Android 的 JNI 函数
  3. 进入 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 的世界,做了以下事情:

  1. 通过 registerServerSockert 方法来创建一个 Server 端的 Socket,这个 name 为 zygote 的 socket 用于等待 AMS 请求 Zygote 来创建新的应用程序进程
  2. 预加载资源,Preload Resources,在 Zygote 进程预加载系统资源后,然后通过它孵化出其他的虚拟机进程,进而共享虚拟机内存和框架层资源,这样大幅度提高应用程序的启动和运行速度
  3. 启动 System Server
  4. 进入 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;
		}
}

注意细节:

  1. Zygote 在 fork 的时候要保证是单线程的,为了避免造成死锁和状态不一致等问题
  2. Zygote 的 IPC 没有采用 Binder,而是本地 Socket

问题:

  1. 孵化应用进程这种事为什么不交给 SystemServer 来做,而专门设计一个 Zygote ?

我们知道,应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类资源系统等等,这些都是非常耗时的,如果能在 zygote 里就给这些必要的初始化工作做好,子进程在 fork 的时候就能直接共享,那么这样的话效率就会非常高。这个就是 zygote 存在的价值,这一点 SystemServer 是替代不了的,主要是因为 SystemServer 里跑了一堆系统服务,这些是不能继承到应用程序的。而且我们应用程序在启动的时候,内存空间除了必要的资源外,最好是干干净净的,不要继承一些乱七八糟的东西,所以呢,不如给 SystemServer 和应用进程里都要用到的资源抽出来单独放到一个进程里,也就是这个 zygote 进程,然后 zygote 进程在分别孵化出 SystemServer 进程和应用进程。孵化出来之后,SystemServer 进程和应用程序进程就可以各干各事了。

  1. Zygote 的 IPC 通信机制为什么不采用 Binder?如果采用 Binder 的话会有什么问题呢?

https://www.zhihu.com/question/312480380

https://blog.csdn.net/qq_39037047/article/details/88066589

深刻理解 Zygote 的工作原理