E95DC099-1CC3-412b-BC1C-C61351E1DD55.png

zygote的作用是什么

1.启动SystemServer
2.孵化应用进程
将从zygote继承来的资源有(常用类 JNI函数 主题资源 共享库**),这样不用重新再加载,对性能有很大提升。**
_

zygote的启动流程

安卓进程启动三段式

C30C3C97-5E33-4c5a-B206-8608D5724CFB.png

进程是怎么启动的?

B42B.png
init进程是系统中的第一个进程,init进程会去加载init.rc配置,init.rc配置包含需要启动的进程信息,通过fork+execve系统调用来启动Zygote进程
b43b.png

4b5b.png

上图方框为一个函数,函数将返回两次,一次在父进程,一次在子进程,在父进程中的pid是启动的子进程的pid,子进程中返回的pid为0.默认来说创建的子进程继承了父进程的所有资源,如果子进程执行了execve系统调用去加载了其他的二进制程序的话,你继承的父进程资源将被新加载的二进制程序替换掉。

4b6b.png
如果子进程挂掉了,父进程将收到SIGCHLD信号,父进程将重现创建子进程

启动之后做了什么事?

Zygote的Native世界

52D83653-52A0-41ad-9F9E-7EA270CF97C2.png
935F835D-4137-441e-BA79-9497CC6AAF07.png

Zygote的Java世界

在Zygote的Java入口Main函数中,先预加载资源,然后fork创建SystemServer,最后创建LOOP循环监听socket请求,当监听到创建子进程请求后,将调用runOnce环境来创建子进程。
E4977896-ADFE-4f04-9EF0-12C84FFAEB68.png

A456C716-016C-42e6-ACBA-14834796CBF6.png
runOnce函数创建子进程
readArgumentList函数,获取创建子进程的参数
调用Zygote.forkAndSpecialize()来创建子进程
当pid=0时,即在子线程中调用handleChildProc函数来执行ActivityThread.main函数,可以理解为主线程的入口函数(不太准确,后面修改)

总叙

注意的细节

image.png
Zygote内部不是单线程,还运行了其他的守护线程,fork创建子进程时,需要停掉其他线程,保证单线程。原因是,子线程也多线程可能导致线程死锁等很多问题。

课后思考问题

057864B6-3B61-4328-99C6-943B8811E815.png

同学你好,
关于第一个问题,
孵化应用进程这种事为什么不交给SystemServer来做,而专门设计一个Zygote?
我们知道,应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类系统资源等等,这些都是非常耗时的,如果能在zygote里就给这些必要的初始化工作做好,子进程在fork的时候就能直接共享,那么这样的话效率就会非常高。这个就是zygote存在的价值,这一点呢systemServer是替代不了的,主要是因为systemServer里跑了一堆系统服务,这些是不能继承到应用进程的。而且我们应用进程在启动的时候,内存空间除了必要的资源外,最好是干干净净的,不要继承一堆乱七八糟的东西。所以呢,不如给systemServer和应用进程里都要用到的资源抽出来单独放在一个进程里,也就是这的zygote进程,然后zygote进程再分别孵化出systemServer进程和应用进程。孵化出来之后,systemServer进程和应用进程就可以各干各的事了。
再看第二个问题,
Zygote的IPC通信机制为什么不采用binder?如果采用binder的话会有什么问题么?
关于为什么不采用binder,有两个原因,
先看第一个原因,我们可以设想一下采用binder调用的话该怎么做,首先zygote要启用binder机制,需要打开binder驱动,获得一个描述符,再通过mmap进行内存映射,还要注册binder线程,这还不够,还要创建一个binder对象注册到serviceManager,另外AMS要向zygote发起创建应用进程请求的话,要先从serviceManager查询zygote的binder对象,然后再发起binder调用,这来来回回好几趟非常繁琐,相比之下,zygote和systemServer进程本来就是父子关系,对于简单的消息通信,用管道或者socket非常方便省事,如果对管道和socket不了解的话,可以参考APUE和UNP这两本书。
再看第二个原因,如果zygote启用binder机制,再fork出systemServer,那么systemServer就会继承了zygote的描述符以及映射的内存,这两个进程在binder驱动层就会共用一套数据结构,这显然是不行的,所以还得先给原来的旧的描述符关掉,再重新启用一遍binder机制,这个就是自找麻烦了。
总的来说呢,对于轻量级的跨进程消息通信,没必要杀鸡用牛刀,普通的管道或者socket足矣。