2.2 Zygote进程启动过程
在2.1节中我们学习了init进程启动过程,在启动过程中主要做了三件事,其中一件就是创建了Zygote 进程,本节接着学习Zygote 进程启动过程,首先我们要了解Zygote是什么。
2.2.1 Zygote概述
在Android系统中,DVM(Dalvik虚拟机)和ART、应用程序进程以及运行系统的关键服务的SystemServer进程都是由Zygote进程来创建的,我们也将它称为孵化器。它通过fock(复制进程)的形式来创建应用程序进程和SystemServer进程,由于Zygote进程在启动时会创建DVM或者ART,因此通过fock而创建的应用程序进程和SystemServer进程可以在内部获取一个DVM或者ART的实例副本。
我们已经知道Zygote进程是在init进程启动时创建的,起初Zygote进程的名称并不是叫“zygote”,而是叫“app_process”,这个名称是在Android.mk中定义的,Zygote进程启动后,Linux系统下的pctrl系统会调用app_process,将其名称换成了“zygote”。
2.2.2 Zygote启动脚本
在init.rc文件中采用了Import类型语句来引入Zygote启动脚本,这些启动脚本都是由Android初始化语言(Android Init Language)来编写的:
可以看出init.rc不会直接引入一个固定的文件,而是根据属性ro.zygote的内容来引入不同的文件。
从Android 5.0开始,Android开始支持64位程序,Zygote也就有了32位和64位的区别,所以在这里用ro.zygote属性来控制使用不同的Zygote启动脚本,从而也就启动了不同版本的Zygote进程,ro.zygote属性的取值有以下4种:
· init.zygote32.rc
· init.zygote32_64.rc
· init.zygote64.rc
· init.zygote64_32.rc
这些Zygote 启动脚本都放在system/core/rootdir 目录中,为了更好地分析这些Zygote启动脚本,我们先来回顾一下2.1.3节所提到的Android初始化语言的Service类型语句,它的格式如下所示:
了解了Service类型语句的格式,下面分别介绍这些Zygote启动脚本。
1.init.zygote32.rc
表示支持纯32位程序,init.zygote32.rc文件内容如下所示:
根据Service类型语句的格式,可以得知Zygote进程名称为zygote,执行程序为app_process,classname为main,如果audioserver、cameraserver、media等进程终止了,就需要进行restart(重启)。
2.init.zygote32_64.rc
表示既支持32位程序也支持64位程序,init.zygote32_64.rc文件的内容如下所示:
脚本中有两个Service类型语句,说明会启动两个Zygote进程,一个名称为zygote,执行程序为app_process32,作为主模式;另一个名称为zygote_secondary,执行程序为app_process64,作为辅模式。
剩余的init.zygote64.rc和init.zygote64_32.rc与上面讲到的Zygote启动脚本类似,这里就不再赘述了,我们在2.1.3节分析的Zygote启动脚本就是支持64位程序的init.zygote64.rc。
2.2.3 Zygote进程启动过程介绍
从2.1.5节中可知init启动Zygote时主要是调用app_main.cpp的main函数中的AppRuntime的start方法来启动Zygote进程的,我们就先从app_main.cpp的main函数开始分析,Zygote进程启动过程的时序图如图2-1所示。
图2-1 Zygote进程启动过程的时序图
Zygote 进程都是通过fock 自身来创建子进程的,这样Zygote 进程以及它的子进程都可以进入app_main.cpp的main函数,因此main函数中为了区分当前运行在哪个进程,会在注释1处判断参数arg中是否包含了“--zygote”,如果包含了则说明main函数是运行在Zygote进程中的并在注释2处将zygote设置为ture。同理在注释3处判断参数arg中是否包含了“--start-system-server”,如果包含了则说明main函数是运行在SystemServer进程中的并在注释4处将startSystemServer 设置为true。
在注释5处,如果zygote为true,就说明当前运行在Zygote进程中,就会调用注释6处的AppRuntime的start函数,如下所示:
在注释1处调用startVm函数来创建Java虚拟机,在注释2处调用startReg函数为Java虚拟机注册JNI方法。注释3处的className的值是传进来的参数,它的值为com.android.internal.os.ZygoteInit。在注释4处通过toSlashClassName函数,将className的“.”替换为“/”,替换后的值为com/android/internal/os/ZygoteInit,并赋值给slashClassName,接着在注释5处根据slashClassName找到ZygoteInit,找到了ZygoteInit后顺理成章地在注释6处找到ZygoteInit的main方法。最终会在注释7处通过JNI调用ZygoteInit的main方法。这里为何要使用JNI呢?因为ZygoteInit的main方法是由Java语言编写的,当前的运行逻辑在Native中,这就需要通过JNI来调用Java。这样Zygote就从Native层进入了Java框架层。
在我们通过JNI调用ZygoteInit的main方法后,Zygote便进入了Java框架层,此前是没有任何代码进入Java框架层的,换句话说是Zygote开创了Java框架层。该main方法代码如下:
在注释1处通过registerServerSocket 方法来创建一个Server端的Socket,这个name为“zygote”的Socket用于等待ActivityManagerService请求Zygote来创建新的应用程序进程,关于AMS将在第6章进行介绍。在注释2处预加载类和资源。在注释3处启动SystemServer进程,这样系统的服务也会由SystemServer进程启动起来。在注释4处调用ZygoteServer的runSelectLoop方法来等待AMS请求创建新的应用程序进程。由此得知,ZygoteInit的main方法主要做了4件事:
(1)创建一个Server端的Socket。
(2)预加载类和资源。
(3)启动SystemServer进程。
(4)等待AMS请求创建新的应用程序进程。
其中第二件事预加载类和资源这里不做介绍,有兴趣的读者可以查看源码,这里会对其他的主要事件进行分析。
1.registerZygoteSocket
首先我们来查看ZygoteServer的registerZygoteSocket方法做了什么,如下所示:
在注释1处拼接Socket的名称,其中ANDROID_SOCKET_PREFIX的值为“ANDROID_SOCKET_”,socketName的值是传进来的值,等于“zygote”,因此fullSocketName的值为“ANDROID_SOCKET_zygote”。在注释2处将fullSocketName转换为环境变量的值,再在注释3处转换为文件描述符的参数。在注释4处创建文件描述符,并在注释5处传入此前转换的文件操作符参数。在注释6处创建LocalServerSocket,也就是服务器端的Socket,并将文件操作符作为参数传进去。在Zygote进程将SystemServer进程启动后,就会在这个服务器端的Socket上等待AMS请求Zygote进程来创建新的应用程序进程。
2.启动SystemServer进程
接下来查看startSystemServer函数,代码如下所示:
注释1处的代码用来创建args数组,这个数组用来保存启动SystemServer的启动参数,其中可以看出SystemServer进程的用户id 和用户组id被设置为1000,并且拥有用户组1001~1010、1018、1021、1032、3001~3010的权限;进程名为system_server;启动的类名为com.android.server.SystemServer。在注释2处将args数组封装成Arguments对象并供注释3处的forkSystemServer函数调用。在注释3处调用Zygote的forkSystemServer方法,其内部会调用nativeForkSystemServer 这个Native 方法,nativeForkSystemServer方法最终会通过fork函数在当前进程创建一个子进程,也就是SystemServer 进程,如果forkSystemServer方法返回的pid的值为0,就表示当前的代码运行在新创建的子进程中,则执行注释4处的handleSystemServerProcess来处理SystemServer进程,关于SystemServer进程启动会在2.3节进行介绍。
3.runSelectLoop
启动SystemServer进程后,会执行ZygoteServer的runSelectLoop方法,如下所示:
注释1处的mServerSocket就是我们在registerZygoteSocket函数中创建的服务器端Socket,调用mServerSocket.getFileDescriptor()函数用来获得该Socket的fd字段的值并添加到fd列表fds中。接下来无限循环用来等待AMS请求Zygote进程创建新的应用程序进程。在注释2处通过遍历将fds存储的信息转移到pollFds数组中。在注释3处对pollFds进行遍历,如果i==0,说明服务器端Socket 与客户端连接上了,换句话说就是,当前Zygote进程与AMS建立了连接。在注释4处通过acceptCommandPeer方法得到ZygoteConnection类并添加到Socket连接列表peers中,接着将该ZygoteConnection的fd添加到fd列表fds中,以便可以接收到AMS发送过来的请求。如果i的值不等于0,则说明AMS向Zygote进程发送了一个创建应用进程的请求,则在注释5处调用ZygoteConnection的runOnce函数来创建一个新的应用程序进程,并在成功创建后将这个连接从Socket连接列表peers和fd列表fds中清除。
2.2.4 Zygote进程启动总结
Zygote进程启动共做了如下几件事:
(1)创建AppRuntime并调用其start方法,启动Zygote进程。
(2)创建Java虚拟机并为Java虚拟机注册JNI方法。
(3)通过JNI调用ZygoteInit的main函数进入Zygote的Java框架层。
(4)通过registerZygoteSocket方法创建服务器端Socket,并通过runSelectLoop方法等待AMS的请求来创建新的应用程序进程。
(5)启动SystemServer进程。