Zygoto的作用
Zygoto的作用有两个:
1. 启动SystemServer
2. 孵化应用进程
SystemServer也是通过Zygoto来启动的,因为SystemServer需要Zygoto初始化好的一些系统资源,包括常用的类、JNI函数、共享库等等,这些资源直接从Zygoto继承过来对启动SystemServer是一件性能提升的事情。
Android中进程启动的通用流程 - 启动三段式
进程启动 -> 准备工作 -> Loop循环
Loop循环的作用是为了接收消息并处理消息。在Android中,所有的独立进程的启动都是符合这个启动流程的。
Zygoto的启动流程
Linux启动之后,用户空间的第一个进程是Init进程,Init进程在启动之后会去读取init.rc的启动配置文件,其中定义了需要启动的系统进程(其中包含Zygoto进程)。Init进程启动Zygoto的方式是fork + execve的方式。
进程启动通常有两种方式:
1. fork + handle
2. fork + execve
【说明】
1. fork函数在创建进程的时候,会返回两次,返回两个pid,当pid = 0的时候,说明当前处于子进程,当pid不为0表示当前处于父进程中,而此时的pid为子进程的pid;
2.fork函数创建的进程继承了父进程所有的系统资源,我们在fork出的子进程中调用execve方法加载二进制文件可以重置继承自父进程的系统资源。
信号处理 - SIGCHLD
父进程通过fork创建的子进程,当子进程挂了之后,父进程会收到一个SIGCHLD的信号,此时我们就可以在父进程中重启子进程。
Zygoto在创建之后做了些什么
1. Zygoto在native世界
2. Zygoto在Java世界
Zygoto是C++编写的,通过main函数入口运行,Zygoto在native做了3件事情,这3件事情都是为了进入Java世界作准备:
从上图可以看到,Zygoto启动之后,首先启动Android的虚拟机,然后注册一些Android的关键的JNI函数,最后通过调用JNI函数进入Java世界。
上面的代码就是Zygoto启动之后做的事情,从上面的代码可以看到,Zygoto首先通过JNI_CreateJavaVM创建虚拟机,在找到ZygotoInit的Java类文件,并获取到该Java类的Main静态函数,最后通过CallStaticVoidMethod方法调用Java类ZygotoInit的Main静态方法。
我们在应用进程中调用JNI的时候,是不需要创建虚拟机的,即不需要调用JNI_CreateJavaVM方法,这是因为虚拟机已经在Zygoto中创建完毕,且应用进程是有Zygoto孵化的,应用进程继承了Zygoto所有的系统资源,同样包含虚拟机,所以在应用进程中调用JNI函数前,无需再次调用JNI_CreateJavaVM来创建虚拟机。
Zygoto在Java世界中要做的事情有3件:
1. 加载资源,这是为了在后续孵化子进程的时候,可以将加载的系统资源继承给子进程;
2. 通过fork函数孵化SystemServer进程
3. 启动Loop循环,接收并处理Socket消息
Zygoto启动Loop循环之后,会接收Socket消息,每次接收到Socket消息之后,就会调用runOnce方法处理消息:
上面的代码是Loop中一次消息处理的过程,首先会通过readArgumentList方法读取本次通过Socket传递过来的参数,然后fork启动一个子进程,再在子进程中处理参数。
子进程是通过handleChildProc方法来处理参数,handleChildProc本质上就是调用某个Java类的main函数,执行的是哪个Java类呢,执行的Java类的名称是通过AMS(Activity Manager Server)借助Socket发过来的,此处通过readArgumentList读取到的。
注意的细节:
1. Zygoto fork需要单进程
Zygoto通过fork创建的子进程中只包含一个线程,而Zygoto中包含多个线程(e.g. 守护线程等等),这些多余的线程在创建完成的子线程中是不存在的,就会造成子进程中状态的错误,所以在Zygoto fork新的子进程的时候,会将所有主线程以外的子线程全部暂停,fork完毕之后,再启动所有的子线程。
2. Zygoto的IPC(跨进程通宵)没有采用Binder机制,而是使用本地的Socket服务。Zygoto进程并没有Binder机制,所以Zygoto创建的应用进程中也没有继承到Binder,应用进程的Binder是应用进程启动之后自己创建启动的。