Zygote是Android系统中的进程,由用户空间的第一个进程Init进程启动的,是Android系统运行的第一个AndroidRuntime进程,同时也是打通Native和Java的桥梁。
Zygote进程作用
Zygote进程的作用主要有两个:
创建SystemServer进程;
孵化其他应用程序进程;
Zygote进程启动流程
Zygote进程是由Init进程解析init.zygote.rc文件启动的,zygote所对应的可执行程序app_process,所对应的源文件是App_main.cpp,进程名为zygote。
首先执行App_main.cpp的main()函数,在这里会执行以下步骤:
1.解析命令参数,主要是--zygote 和--start-system-server。
2.调用AppRuntime.start("com.android.internal.os.ZygoteInit",...),启动虚拟机,注册JNI函数;
3.通过JNI调用ZygoteInit.java的main()方法,从这里开始进入Java的世界;
运行Zygote的main方法,主要执行以下步骤:
预加载 preloadClasses()、preloadResources()、preloadSharedLibraries()
forkSystemServer,最终会调用SystemServer.java的main()方法;
创建ZygoteService,进入runSelectLoop;
Zygote 的工作原理
我们知道 Zygote 进程的两大作用就是:启动 SystemServer; 孵化应用进程,今天来看下它是如何工作的。
我们先来复习下,Zygote 启动后到 java 世界执行的代码【28.0源码】:
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java |
启动 SystemServer
如果 app_process 的调用参数中带有”—start-system-server”,那么此时会通过 forkSystemServer 来启动。这里稍后再看,先考虑一个问题:
Zygote 在前期主要担任启动系统服务的工作,后期则又担当“孵化进程”的重任,但是 Zygote 只在 init.rc 中被启动一次,它如何协调好这两项工作呢?通过 forkSystemserver 这个方法名,我们可以明显看出这是启动了一个新的进程来承载系统服务,而我们推断 app_process 所在进程会继续执行,从而转变为 Zygote 的“孵化器”守护进程。接下来我们来看是不是这样的。
forkSystemServer
还记得 Zygote 启动时,是使用 fork + execve 方式,而此处不同使用 fork + handle (即直接继承资源,未使用 execve 进行资源覆盖),简单表示如下:
接着来看,handleSystemServerProcess 方法里最后调用的是 ZygoteInit.zygoteInit 方法:
这个方法比较短,核心就上面三行,我们来介绍一下:
commonInit: 通用部分的初始化,包括设置默认的 uncaught exception handler;TimeZone,LogManager,AndroidConfig, HTTP User-Agent 等模块的初始化。
nativeZygoteInit: 本地初始化函数,也是 zygoteInit 中的重点
applicationInit: 它是 SystemServer 的“起点”,而参数 argv 里会带有 startClass 和 startArgs。这两个变量是在 ZygoteInit.forkSystemServer 方法定义并传入的参数。实际上最终将调用 SystemServer的 main 方法
经过这个 zygoteInit 方法之后,程序现在有两个分支,其一是 nativeZygoteInit 主导的 native 系统服务启动;另一个则是 applicationInit 负责的 Java 层系统服务启动。
继续深入
native 系统服务启动
nativeZygoteInit 对应源码如下:
gCurRuntime 是一个全局的 AndroidRuntime 对象,它的实现在 App_main.cpp 中的 AppRuntime,onZygoteInit 方法自然也在 App_main 里了:
这段代码是 Binder 机制中的重要组成部分,其中 startTheardPool 将开启 Binder 线程池,以保证其它进程可以正确访问的 Zygote 所提供的服务。Zygote 通过 JNI 和回调的方式非常巧妙的把 Native 层和 Java 层、SystemServer 和 app process 关联起来了。
Java 层系统服务的启动
System Server 在启动前首先需要做很多初始化设置,包括将 VM 的版本记录到系统变量中,设置线程优先级,设置堆的使用率。
我们知道,Android 的系统服务也被分为两类,其中,Native 层通过 System.loadLibrary(“android_servers”) 加载,而 java 层又继续细分如下:
startBootstrapServices() “引导程序”服务,代表系统服务中最核心的部分,如 ActivityManagerService, Power Manager, Display Manger, PackageManger 等
startCoreServices() 次核心,包括 LED 和背光管理器,电池电量管理器,应用程序使用情况(Usage Status)管理器等
startOtherServices() 优先级较低,但是 Service 数最多,如 AccountManagerService, VibratorService, MountService, NetworkManagementService, WindowMangerService, AudioService 等。这些服务一起构建起 Android System Server 这座“参天 大厦”,为其它进程、应用程序的正常运行奠定了基础。
最后,SystemServer 通过 Looper.loop() 进入死循环,并依托 onZygoteInit 中启动的 Binder 服务接受和处理外界的请求。
至此 Zygote 启动 SystemServer 部分的工作原理已经介绍完了。接下来看 Zygote 又是怎么孵化应用进程的。
孵化应用进程
让我们回到本文最开始的地方。Zygote 启动后到 java 世界执行的代码:
runSelectLoop 从方法名可以猜到,这应该是一个死循环,除非 Zygote 退出或出现异常才会跳出循环:
我们看到 runSelectLoop 的主体的确是一个 while 死循环,它将作为 zygote 的守护体存在。
mServerSocket.getFileDescriptor() 获取到的是前面通过 registerServerSocketFromEnv 创建的 Server Socket 的文件描述符。但它没有回到 fds 中,这同时也说明了 zygote 中不止有一个文件描述符来与 zygotę 通讯,通过后面的 for 循环,找出当前可读的 fd 。
i == 0 时
fds 中,添加的第一个元素为 Zygote 的 Server Socket,所以此时表示有新的连接请求,这和网络连接中的 Socket 概念是一致的。
我们通过 acceptCommandPeer 接受一个新的客户羰连接,产生一个新的 ZygoteConnection ,然后更新 peers 和 fds 这样值班表中对象的 index 就是一致。(这就是为啥前面 peers.add(null))
i > 0 时
说明已经建立 Socket 连接中有来自客户端的数据需要处理,具体的工作就是 processOneCommand 。
这边同样是典型的 使用 fork + handle 模式来处理 fork 进程。不用细看也知道做了两件事:
创建承载应用程序的新进程值得注意的是,这边没有直接用 fork 方法来启动新进程,而调用了 forkAndSpecialize 方法。Specialize 意为 “特殊化”,其实就是在 fork 的同时也把它转变为 Android 应用程序。
AMS 向 Zygote 发起创建进程请求时传入的参数,其中有最重要的参数之一是 className (android.app.ActivityThread), handleChildProc 会把这个类加载到内存中,然后执行它的 main 函数。换句话说,我们熟悉的 Android 应用程序的“主线程”就此启动了
父进程的扫尾工作handleParentProc 包括将子进程加入进程组,正确关闭文件,调用方返回结果值等
至此,我们也讲完了 Zygote 作为守护进程时,孵化 Android 应用程序进程的工作原理。
总结
孵化应用进程为什么不给SystemServer来做,而专门设计一个Zygote?
【答】我们知道,应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载各类系统资源等等,这些都是非常耗时的,如果能在zygote里就给这些必要的初始化工作做好,子进程在fork的时候就能直接共享,那么这样的话效率就会非常高。这个就是zygote存在的价值,这一点呢SystemServer是替代不了的,主要是因为SystemServer里跑了一堆系统服务,这些是不能继承到应用进程的。而且我们应用进程在启动的时候,内存空间除了必要的资源外,最好是干干净净的,不要继承一堆乱七八糟的东西。所以呢,不如给SystemServer和应用进程里都要用到的资源抽出来单独放在一个进程里,也就是这的zygote进程,然后zygote进程再分别孵化出SystemServer进程和应用进程。孵化出来之后,SystemServer进程和应用进程就可以各干各的事了。
Zygote的IPC通信机制为什么不采用binder?如果采用binder的话会有什么问题?
【答】第一个原因,我们可以设想一下采用binder调用的话该怎么做,首先zygote要启用binder机制,需要打开binder驱动,获得一个描述符,再通过mmap进行内存映射,还要注册binder线程,这还不够,还要创建一个binder对象注册到serviceManager,另外AMS要向zygote发起创建应用进程请求的话,要先从serviceManager查询zygote的binder对象,然后再发起binder调用,这来来回回好几趟非常繁琐,相比之下,zygote和SystemServer进程本来就是父子关系,对于简单的消息通信,用管道或者socket非常方便省事。第二个原因,如果zygote启用binder机制,再fork出SystemServer,那么SystemServer就会继承了zygote的描述符以及映射的内存,这两个进程在binder驱动层就会共用一套数据结构,这显然是不行的,所以还得先给原来的旧的描述符关掉,再重新启用一遍binder机制,这个就是自找麻烦了。