目录
Zygote进程是怎么启动的?
Linux启动后启动的第一个用户进程是Init进程,Init进程启动后会加载启动配置文件(init.rc),判断配置文件中定义了哪些配置服务是需要启动的,Zygote就是需要启动的服务之一。
启动进程的方式有两种:
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函数,fork函数会返回两次,分别在子进程和父进程中各返回一次
- 在子进程中pid返回0
- 在父进程中pid返回子进程的pid
根据pid是否为0来判断当前是子进程还是父进程
子进程默认继承了父进程的所有资源,如果调用了execev系统调用去加载另外的二进制程序,继承的父进程资源就会被新加载的资源替换掉
信号处理-SIGCHLD
父进程fork出了子进程,如果子进程挂掉了,则父进程会收到SIGCHLD信号,这时可以去做一些处理。
例如Zygote如果挂掉了,这时候Init进程将会收到SIGCHLD信号,然后去重启Zygote进程。
Zygote进程启动后做了什么?
Zygote的Native世界
- 启动Android虚拟机
- 注册Android的JNI函数
- 进入Java世界
Native切换到Java
int main(int argc,char *argv[]){
JavaVM *jvm;
JNIEnv *env;
JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args); //创建Java虚拟机
jclass clazz = env->FindClass("ZygoteInit"); //找到叫ZygoteInit的Java类
jmethodID method = env->GetStaticMethodID(clazz,"Main","[Ljava/lang/String;)V"); //找到ZygoteInit类中的Main的静态函数
env->CallStaticVoidMethod(clazz,method,args); //调用main函数
jvm->DestroyJavaVM();
}
Zygote的Java世界
//ZygoteInit.java
public static void main(String argv[]){
//1.预加载frameworks/base/preloaded-classes和framework_res.apk资源,linux在fork进程时,只是为子进程创建一个数据结构,使子进程地址空间映射到与父进程相同的物理内存空间。虚拟机的创建也是实例拷贝,共享系统资源,如果每创建一个虚拟机都加载一份系统资源,将会非常耗时和浪费。子进程在刚fork生成时,完全共享父进程的进程物理空间,采用写时复制的方式共享资源。
preloadClasses();
preloadResources();
preloadSharedLibraries();
// 2. 启动system_server进程。该进程是framework的核心。
if(argv[1].equals("start-system-server")){
startSystemServer();
}
//3.创建Socket服务
registerZygoteSocket();
//4.进入Loop循环,等待连接(socket消息),用以处理来自AMS申请进程创建的请求
runSelectLoopMode();
}
进入Loop循环后,如果接收到socket请求,则会执行runOnce
boolean runOnce() {
String[] args = readArgumentList(); //读取参数列表
int pid = Zygote.forkAndSpecialize(); //根据读取到的参数启动子进程
if(pid == 0) {
//in child
//执行ActivityThread的入口函数(main)
handleChildProc(args,...);
return true;
}
}
注意
- Zygote fork要单线程
- Zygote的IPC是采用socket
相关问题
孵化应用进程为什么不交给SystemServer来做,而是专门设计一个Zygote?
我们知道,应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类系统资源等等,这些都是非常耗时的,如果能在zygote里就给这些必要的初始化工作做好,子进程在fork的时候就能直接共享,那么这样的话效率就会非常高。这个就是zygote存在的价值,这一点呢systemServer是替代不了的,主要是因为systemServer里跑了一堆系统服务,这些是不能继承到应用进程的。而且我们应用进程在启动的时候,内存空间除了必要的资源外,最好是干干净净的,不要继承一堆乱七八糟的东西。所以呢,不如给systemServer和应用进程里都要用到的资源抽出来单独放在一个进程里,也就是这的zygote进程,然后zygote进程再分别孵化出systemServer进程和应用进程。孵化出来之后,systemServer进程和应用进程就可以各干各的事了。
Zygote的IPC通信机制为什么不采用binder?如果采用binder会有什么问题?
因为服务端的Binder必须运行在线程池中,而客户端在连接服务端的Binder时会导致本线程挂起,如果服务端Binder意外死亡,会导致客户端本线程死锁。另外Linux的fork()方法不允许多线程。
Zygote的作用是什么?
- 预加载需要使用到的类和系统资源,子进程在刚fork的时候共享父进程的资源,加快了应用程序的启动速度
- 启动SystemServer
- 启动Loop循环用于监听AMS发送过来的消息,然后调用ActivityThread来启动相对应的app