Android Framework 面试系列(一)Zygote

262 阅读3分钟

Zygote 的作用

在 Android 中,Zygote 进程主要有三个作用,分别是:

  • 预加载系统资源,比如常用类、JNI函数、主题资源、共享库等
  • 启动 SystemServer
  • 孵化应用进程

Zygote 的启动流程

Zygote 进程启动流程

在 Android 中,Zygote 进程是通过 Init 进程启动的。Init进程是Linux系统中用户空间的第一个进程,进程号固定为1,启动代码在 system/core/init/main.cpp 中。

image.png

如上图所示,Init 进程启动 Zygote 进程的流程有三步:

  • 第一步:读取 init.rc 启动配置文件
  • 第二步:根据配置文件,使用 fork 系统调用来创建 Zygote 进程
  • 第三步:执行 execve 系统调用来加载并执行 Zygote 程序

fork 和 execve 都是 linux 的系统调用。其中 fork 系统调用用于创建一个新的进程,称为子进程。子进程是父进程的一个副本,它几乎复制了父进程的所有资源,包括代码段、数据段、堆、栈、打开的文件描述符等。execve 系统调用的主要作用是在当前进程中加载并执行一个新的可执行程序。一般和 fork 配合使用,用于实现进程功能的切换。

Zygote 进程启动之后做了什么

image.png

从上图可以看到,Zygote 进程启动后,主要做了三件事情:

  1. 启动Android 虚拟机。启动虚拟机是在 Zygote Native 世界,在启动完虚拟机后,Zygote 会注册Android 的JNI函数,然后进入 Java 世界。
  2. 启动 System Server(在另一个进程)。进入 Java 世界后,会先预加载系统资源,然后启动SystemServer
  3. 进入 Loop 循环。在 Loop 循环中,Zygote 等待接收创建应用进程的请求(socket 请求)。Zygote 循环内部采用 I/O 多路复用机制,保证在没有客户端连接请求或数据处理时休眠,

相关面试题

Zygote 是怎么启动的

  1. init 进程 fork 出 zygote 进程
  2. 启动虚拟机,注册jni函数
  3. 预加载系统资源
  4. 启动SystemServer
  5. 进入 Socket Loop

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

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

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

主要原因是因为Zygote进行fork的时候要是单线程,父进程binder线程有锁,然后子进程的主线程一直在等其子线程(从父进程拷贝过来的子进程)的资源,但是其实父进程的子进程并没有被拷贝过来,造成死锁,所以fork不允许存在多线程。而非常巧的是Binder通讯偏偏就是多线程,所以干脆父进程(Zygote)这个时候就不使用binder线程。

谈谈你对 zygote 的理解

如下所示,对于这类问题,我们需要从 what、how、why 三方面考虑。

  • what:zygote的作用是什么
  • how:zygote 的启动流程是什么
  • why:zygote的工作原理是什么?它是怎么孵化进程的?怎么和别人通信的?