Framework的启动过程

1.Framework运行环境综述

  Framework的运行环境如下图:
Dalvik虚拟机进程间的关系

  系统中运行的第一个Dalvik虚拟机程序叫做zygote,该名称的意义是“一个卵”,因为接下来的所有Dalvik虚拟机进程都是通过这个“卵”孵化出来的。
  zygote进程中包含两个主要模块,分别如下:

  • Socket服务端:该Socket服务端用于接收启动新的Dalvik进程的命令;
  • Framework共享类及共享资源:当zygote进程启动后,会装载一些共享的类及资源,其中共享类是在preload-classes文件中被定义,共享资源是在preload-resources中被定义。因为zygote进程用于孵化其他Dalvik进程,因此,这些类和资源装载后,新的Dalvik进程就不需要再装载这些类和资源了,这就是所谓的共享。
      zygote进程对应的具体程序是app_process,该程序存在于system/bin目录下,启动该程序的指令是在init.rc中进行配置的。
      zygote孵化出的第一个Dalvik进程叫做SystemServer,SystemServer仅仅是该进程的别名,而该进程具体对应的程序依然是app_proces,因为SystemServer是从app_process中孵化出来的。
      SystemServer中创建一个Socket客户端,并有Ams负责管理该客户端,之后所有的Dalvik进程都将通过该Socket客户端间接被启动。当需要启动新的APK进程是,Ams中会通过该Socket客户端向zygote进程的Socket服务端发送一个启动命令,然后zygote会孵化出新的进程。
      从系统架构的角度来讲,就在于此即先创建一个zygote,并加载共享类和资源,然后通过该zygote去孵化新的Dalvik进程,该架构的特点有两个:
  • 每一个进程都是一个Dalvik虚拟机,而Dalvik虚拟机是一种类似于Java虚拟机的程序,并且从开发的过程来看,与标准的Java程序开发基本一致。
  • zygote进程预先会装载共享类和共享资源,这些类及资源实际上就是SDK中定义的大部分类和资源。因此,当通过zygote孵化出新的进程后,新的APK进程只需要去装载APK自身包含的类和资源即可,这就有效地解决了多个APK共享Framework资源的问题。

2.Dalvik虚拟机相关的可执行程序

3.zygote的启动

启动Socket服务端口

  当zygote服务从app_process开始启动后,会启动一个Dalvik虚拟机,而虚拟机执行的第一个Java类就是ZygoteInit.java,因此接下来的过程就从ZygoteInit类的main()函数开始说起。main()函数中做的第一个重要工作就是启动一个Socket()服务端口,该Socket端口用于接收启动新进程的命令。
  启动Socket服务端口是在静态函数registerZygoteSocket()中完成的。
Socket编程中有两种方式去触发Socket数据读操作

方式 解释
阻塞式读操作 使用listen()监听某个端口,然后调用read()去从这个端口上读数据,这种方式被称为阻塞式读操作,因为当端口没有数据时,read()函数将一直等待,直到数据准备好后才返回;
非阻塞式读操作 使用select()函数将需要检测的文件描述符作为select()函数的参数,然后当该文件描述符上出现新的数据后,自动触发一个中断,然后在中断处理函数中再去读指定文件描述符上的数据;

  安卓使用的是非阻塞式读操作。

加载preload-classes

  在ZygoteInit类的main()函数中,创建完Socket服务端后还不能立即孵化新的进程,因为这个“卵”中还没有必须“核酸”,这个“核酸”就是指预装的Framework大部分类及资源。
  预装的类列表是在framework.jar中的一个文本文件列表,名称为proload-classes,该列表的原始定义在frameworks/base/proload-classes文本文件中,而该文件又是通过frameworks/base/tools/preload/WritePreloadedClassFile.java类生成的。产生proload-classes的方法是在Android根目录下执行命令生成的。执行命令后,会在frameworks/base目录下产生preload-classes文本文件。从该命令的执行情况来看,预装的Java类信息包含在.compiled文件中,而这个文件却是一个二进制文件。
  在Android源码编译的时候,会最终把preload-classes文件打包到framework.jar中。
  有了这个列表后,ZygoteInit中通过调用preloadClasses()完成装载这些类。装载的方法很简单,就是读取preload-classes列表中的每一行,因为每一行代表了一个具体的类,然后调用Class.forName()装载目标类。

加载preload-resources

  preload-resources是在frameworks/base/core/res/res/values/arrays.xml中被定义的,包含两类资源,一类是drawable资源,另一类是color资源。
  加载这些资源是在preloadResources()函数中完成的,该函数分别调用preloadDrawable()和preloadColorStateLists()加载这两类资源。加载的原理很简单,就是把这些资源读出来放到一个全局变量中,只要该类对象不被销毁,这些全局变量就会一直保存。
  保存Drawable资源的全局变量是mResources,该变量的类型是Resources类,由于该类内部会保存一个Drawable资源列表,因此,实际上缓存这些Drawable资源是在Resources内部;保存Color资源的全局变量也是mResources,同样,Resources类内部也有一个Color资源的列表。

使用folk启动新的进程

  folk是Linux系统的一个系统调用,其作用是复制当前进程,产生一个新的进程。新进程将拥有和原始进程完全相同的进程信息,除了进程id不同。进程信息包括该进程所打开的文件描述符列表、所分配的内存等。当新进程被创建后,两个进程将共享已经分配的内存空间,直到其中一个需要向内存中写入数据时,操作系统才负责复制一份目标地址空间,并将要写的数据写入到新的地址中,这就是所谓的copy-on-write机制,即“仅当写的时候才复制”,这种机制可以最大限度地在多个进程中共享物理内存。
  ZygoteInit.java中复制新进程是通过在runSelectLoopMode()函数中调用ZygoteConnection类的runOnce()函数完成的,而该函数中则调用了forkAndSpecialize()函数用于复制一个新的进程。
  forkAndSpecialize()函数是一个native()函数。
  当新进程被创建好后,还需要做一些“善后”工作。因为当zygote复制新进程时,已经创建了一个Socket服务端,而这个服务端是不应该被新进程使用的,否则系统中会有多个进程接收Socket客户端的命令。因此,新进程被创建好后,首先需要在新进程中关闭该Socket服务端,并调用新进程中指定的Class文件的main()函数作为新进程的入口点。

SystemServer进程的启动

  SystemServer进程是zygote孵化出的第一个进程,该进程是从ZygoteInit.java的main()函数中调用startSystemServer()开始的。与启动普通进程的差别在于,zygote类为启动SystemServer提供了专门的函数startSystemServer(),而不是使用标准的forAndSpecilize()函数,同时,SystemServer进程启动后首先要做的事情和普通进程也有所差别。
  startSystemServer()函数的关键代码有三处。
  第一处:定义了一个String[]数组,数组中包含了要启动的进程的相关信息,其中最后一项指定了新进程启动后装载的第一个Java类,此处即为com.android.server.SystemServer类。
  第二处:调用forkSystemServer()函数从当前的zygote进程孵化出新的进程。
  新进程启动后,首先执行handleSystemServerProcess()函数,这个函数主要完成两件事:第一是关闭Socket服务端,第二是执行com.android.server.SystemServer类的main()函数。除了这两个事情外,还做了一些额外的运行环境配置,这些配置主要是在commonInit()和zygoteInitNative()两个函数中完成。一旦SystemServer的进程环境配置好后,就从SystemServer类的main()函数中开始运行。
启动各种系统服务线程
  SystemServer进程在Android的运行环境中扮演了“神经中枢”的作用,APK应用中能够直接交互的大部分系统都在该进程中运行,常见的比如WindowManagerServer(Wms)、ActivityManagerSystemService(AmS)、PackageManagerServer(PmS)等,这些系统服务都是以一个线程的方式存在于SystemServer进程中。

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-------------------------last line for now---------------------------