大厂Android岗高频面试问题:说说你对Zygote的理解!

前言

Zygote能够说是Android开发面试很高频的一道问题,但总有小伙伴在回答这道问题总不能让面试满意, 在这你就要搞清楚面试问你对Zygote的理解时,面试官最想听到的和其实想问的应该是哪些?下面咱们经过如下几点来剖析这道问题!java

  • 了解Zygote的做用
  • 熟悉Zygote的启动流程
  • 深入理解Zygote的工做原理

下面来咱们来深刻剖析linux

1、 Zygote的做用

Zygote的做用分为两点:android

  • 启动SystemServer
  • 孵化应用进程

关于这个问题答出了这两点那就是OK了。可能大部分小伙伴可能能答出第二点,第一点就不是很清楚。SystemServer也是Zygote启动的,由于SystemServer须要用到Zygote准备好的系统资源包括:ios

img

直接从Zygote继承过来就不须要从新加载过来,那么对性能将会有很大的提高。面试

2、Zygote的启动流程

2.1 启动三段式

在说Zygote启动流程以前,先明确一个概念:启动三段式,这个能够理解为Android中进程启动的经常使用套路,分为三步骤:架构

img

这里要了解LOOP循环是什么,其实LOOP做用是不停的接受消息处理消息,消息的来源能够是SoketMessageQueueBinder驱动发过来的消息,但不管消息从哪里来,它整个流程都是去接受消息,处理消息。这个启动三段式,它不光是Zygote进程是这样的,只要是有独立进程的,好比说系统服务进程,本身的应用进程都是如此。app

2.2 Zygote进程是怎么启动的?

Zygote进程的启动取决于init进程,init进程是它是linux启动以后用户空间的第一个进程,下面看一下启动流程jvm

  1. linux启动init进程
  2. init进程启动以后加载init.rc配置文件

img

  1. 启动配置文件中定义的系统服务,其中Zygote服务就是定义在配置中的

img

  1. 同时启动的服务除了Zygote以外还有一些别的系统服务也是会启动的,好比说ServiceManager进程,它是经过fork+execve系统调用启动的

img

2.2.1加载Zygote的启动配置

在init.rc 文件中会import /init.${ro.zygote}.rc,init.zygoteXX,XX指的是32或者64,对咱们没差咱们直接看init.zygote32.rc便可。配置文件比较长,这里作了截取保留了Zygot相关的部分。socket

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 audioserver    
writepid /dev/cpuset/foreground/tasks
  • service zygote:是进程名称,
  • /system/bin/app_process:可执行程序的路径,用于init进程fork,execve调用
  • -Xzygote /system/bin --zygote --start-system-server 为它的参数

2.2.2启动进程

说完了启动配置呢,这里来聊一下启动进程,启动进程有两种方式:函数

第一种: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,所以能够根据判断pid来区分目前是子进程仍是父进程
  • 对于handle默认的状况,子进程会继承父进程的全部资源,但当经过execve去加载二进制程序时,那父进程的资源则会被清除

2.2.3信号处理-SIGCHLD

当父进程fork子进程后,父进程须要关注这个信号。当子进程挂了,父进程就会收到SIGCHLD,这时候父进程就能够作一些处理。例如Zygote进程若是挂了,那父进程init进程就会收到信号将Zygote进程重启。

img

3、Zygote进程启动原理

主要分为两部分Native层处理和Java层处理,Zygote进程启动以后,它执行了execve系统调用,它执行的是用C++写的二进制的可执行程序里的main函数做为入口,而后在Java层运行!

先来看一下Native层的处理流程

img

在app_main.cpp文件,AndroidRuntime.cpp文件。咱们能够找到几个主要函数名

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();
}

根据上述代码,你会发如今咱们的应用里直接就能够 JNI 调用了,并不须要建立虚拟机。由于应用进程是Zygote进程孵化出来的,继承了父进程的拥有虚拟机,只须要重置数据便可。

接着看一下Java层的处理,具体可参考ZygoteInit文件的main方法

  1. 预加载资源,好比经常使用类库、主题资源及一些共享库等

img

  1. 启动SystemServer进程

img

  1. 进入Socket 的Loop循环 会看到的ZygoteServer.runSelectLoop(…)调用
boolean runOnce() {
	String[] args = readArgumentList(); //读取参数列表
	int pid = Zygote.forkAndSpecialize(); //根据读取到的参数启动子进程
	if(pid == 0) {
		//in child
		//执行ActivityThread的入口函数(main)
		handleChildProc(args,...);
		return true;
	}
}

img

4、总结

Zygote启动流程中须要主要如下2点问题

  1. Zygote fork要保证是单线程
  2. Zygote的IPC是采用socket

看完三件事❤️


若是你以为这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

  1. 点赞,转发,有大家的 『点赞和评论』,才是我创造的动力。
  2. 关注公众号 『 阿风的架构笔记 』,不按期分享原创知识。
  3. 同时能够期待后续文章ing🚀
  4. 关注后回复【666】扫码便可获取架构进阶学习资料包
相关文章
相关标签/搜索