这一期我们来分析一下
Zygote
是如何在我们的服务中创建一个
APP
应用的。
下面我们来看一下
Zygote
创建应用
我们的zygote
再启动过程中,首先会创建一个本地套接字,他就用来接收消息,来创建应用。第二个就是
load
资源,把资源全部
load
进来,包括
class
等。第三就是创建我们的
system server
,然后这个服务会创建所有的系统服务,并且启动我们第一个
lunch
,这个启动过程是在最后一步
runSelectLoop
中实现的。当我们把上三步做完之后,我们的
zygote
就会进入一个
runSelectLoop
函数,在这里边一直监听消息,进来之后我们会首先使用
selectReadable
这个方法,来监听我们的
socket
,如果说我们的
ActivityServiceManager
发送了一个请求过来,首先我们会把这个请求保存在一个数据结构中,然后再去监听,如果说我们的
ActivityServiceManager
发送了一个创建应用的消息过来之后呢,我们就会调用我们的
runOnce
的方法去读取消息,然后通过一系列的操作去创建我们的应用,在这个过程中,我们首先会读取请求的内容,然后调用
forkAndSpecialize
来创建子进程,然后再调用
handleChildproc
创建应用,这就是我们应用的一个启动过程。
下面我们来看一下他的大概流程
首先我们的
zygote
进程会调用我们的
registerZygoteSocket()
来创建一个
socket
,创建完成之后,我们会调用
selectReadable
去监听我们的
socket
,而且还有一个其他的
session
,这个
session
是监听他的客户,我们一会再来看,第一次我们只有一个
socket
我们来监听,如果说有一个其他的进程连接了我们的
socket
,也就是他要启动一个应用,这个时候我们会处理一个
connect
,当我们收到
connect
消息之后,我们会调用
acceptCommandPeer()
,创建一个
session
,并且把这个链接添加到我们的
session
中,这个
session
就是我们在上边监听到的
session
,原来我们的
session
中是没有连接的,现在我们添加了一个链接,添加完之后我们就会再次调用我们的
selectReadable
去做监听,下一步如果说我们这个应用发了一个启动的消息过来之后,我们就会监听到我们的
session
是有消息过来了,这是时我们就会调用我们的
runOnce()
函数去接受我们启动的消息,然后去启一个应用,这个过程首先是把这个应用发过来的消息给读出来,读出来之后,我们就会创建一个子进程,然后在子进程中调用
handleChilProc
这个方法去创建我们的应用,根据我们传进来的参数不同,我们启动应用的方式也分为两种,第一种调用
execApplication()
的方式,直接使用
APPprocess
来启动我们的应用;第二种是使用
MethodAndArgsCaller()
,剖出异常的方式,来启动应用,这就是应用启动的一个过程,当我们创建完成之后,就会把这个链接从我们的
session
中移出来,然后在使用
selectReadable
继续监听,继续读消息,这就是
zygote
创建应用的一个整个流程。
下面我们看一下整个代码的结构
首先打开我们的
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
,然后找到我们的
main
函数,在
main
函数中创建完
socket
函数之后呢就会调用
runSelectLoop
,我们打开它看一下
首先我们会有两个变量,一个是
fds
,一个是
peers
,
fds
是用于保存我们监听的
socket
,而我们的
peers
就是一个
session
。我们首先要把我们的
sServerSocket
添加到我们的
fds
中,然后就进到了我们的循环,在这里我们会调用我们的
selectReadable
,
selectReadable
他真正的实现是一个
native
的方法,他是在我们的
frameworks/base/core/jni/com_android_internal_os_ZygoteInit.cpp
中,我们来看一下,他调用的其实就是我们的
ZygoteInit_selectReadable()
,我们来看一下
这个函数的作用就是用来监听我们的socket
有没有链接、有没有消息过来,首先我们来看第三个参数,这就是我们的文件描述符,首先我们会拿到这个文件描述符的长度,在这里有一个
fd_set
的变量,他是我们
Linux
的一种标准变量,就是为
select
而使用的。然后我们会通过
env->Get0bjectArray
的方法去拿到我们的每一个文件描述符,然后得到他真正的文件描述符,然后见我们的文件描述符添加到我们的
FD_SET
中,这样我们就可以监听每一个文件描述符了,添加完成之后我们就可以调用我们的
select
函数去等待新的链接或者消息,
select
在这里是无限的等待,除非有链接或者消息的到来,然后他只监听读消息,如果我们有新的链接或者消息过来之后,他就会立刻返回,并且在这里得到是哪个文件描述符有消息过来,然后我们来看一下是如何来处理这个返回值的
如果我们的
index
返回的值小于
0
,这个肯定是有错误的。如果等于
0
,那么这个是一个新的链接,为什么会说他是一个新的链接呢,因为我们第一个添加的文件描述符是
sServerSocket
,所以说如果
index
等于
0
,对应我们的是我们的
array
中的
socket
,所以说是一个新的链接,有新的链接之后我们就建立一个新的
session
,并且将这个
session
添加到
peers
中,然后将这个个新的文件描述符添加到我们的
fds
中,然后就又返回来继续监听,如果在有消息过来,那么这个
index
肯定是要大于
0
的,那么我们根据这个
index
保存的
session
的具体的值去调用我们的
runOnce()
,而我们的
runOnce
就是去读取我们的消息,并且根据我们的消息去启动我们的应用程序,
我们首先会调用
readArgumentList()
去读取我们的消息,读完之后我们就会将消息做一系列的处理,然后调用
forkAndSpaceialize
去创建一个新的进程,这个进程是和我们创建
systemserver
的进程是不一样的,因为这是一个应用的进程,我们来看一下他的具体实现
他的具体实现其实是调用nativeForkAndSpecialize
,这个函数呢是在
dalvik/vm/native/dslvik_system_Zygote.cpp
中定义的。它所对应的就是我们的
Dalvik_dalvik_system_Zygote_forkAndSpecialize
,我们来看一下这个函数,这个函数调用的是
forkAndSpecializeCommon
,和我们创建
system
这个函数是一样的,他在这里也是调用的这个函数,在这里就不再细说了,唯一的不同就是在创建
system_server
时,我们会检查有没有创建成功,如果说创建失败的话,
zygote
会重新启动,而我们这个创建应用的函数,他并没有做这个,只是创建了一个子进程,然后直接返回。
当我们把这个进程创建完成之后呢,我们就开始启动我们的服务
如果pid
是
0
,就说明这个是我们的子进程,那么就会调用我们的
handleChildProc
去创建我们的应用,我们来看一下这个函数
首先我们会把我们的
socket
关闭掉,然后做一些清理工作,如果我们的
args
中
runtimeInit
是
true
的话,这说明我们需要建立自己的运行库
在这个分支中,如果说
invokeWith
的方法是没有的,那么我们就使用
system exec
的方式去启动这个应用。