安卓底层开发学习经验第十七期

这一期我们来分析一下 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 的方式去启动这个应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值