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