android 系统大致可以分为四层,由下往上分别为:
- Linux kernel 内核层,android 系统版本基于的Linux 内核版本不尽相同
- Libraries 层,这一层提供动态链接库(也叫共享库)、Android 运行时库、Dalvik 虚拟机等。从编程语言来看,这一层大部分都是 C或C++ 写的,所以也可以简单把这一层看成 Native 层
- Framework 层,这一层大部分都是 Java 语言编写。它是 Android 平台上 Java 世界的基石。
- Application 层,和用户直接交互的就是这些应用程序,他们都是 Java 开发。
通过上面架构图可以可知,Android Runtime 处于Linux 和 framework 层之间,而 Java 的运行环境依赖于 Java 虚拟机,所以 AndroidRuntime 对于 android 系统非常重要。Init 进程是 Linux 环境下非常重要的一个进程,而 Zygote 进程的运行又需要依赖 Java 环境,并且在 Java 环境下其他应用进程都需要 Zygote 进程 frok出来,所以在 init 启动 Zygote 进程之后,初始化 Zygote 进程之前,会先进行 AndroidRuntime 的启动和环境准备。
init 进程
init 是 Linux 系统中用户空间的第一个重要的进程,在android 系统中, 它的进程号是 1。init 程序并不是一个源文件组成的,而是由一组源代码的目标文件链接而成,这些文件目录位置为:/system/core/init/
主要负责:
- 文件系统的挂载,如
/sys、/dev、/proc
等目录 - 初始化属性,提供 property service(属性服务) 管理 Android 系统中的属性
- 处理配置文件命令(加载、解析)(主要是 init.rc 脚本文件)
- 性能分析和执行其他进程。
init 进程的入口是 init.c
文件的 main 函数,主要工作:
- 文件系统挂载、klog 初始化、初始化信号量。
- 加载并解析 init.rc 以及 与硬件平台相关的配置文件(如 init.xxx.rc),在此阶段,也会解析 service。
- 执行各个阶段的动作,创建 zygote 的工作就是在其中某个阶段完成。
- 调用 property_init 初始化属性相关的资源,并通过 property_start_service 启动属性服务。
- init 进入一个无限循环,然后等待响应。
ZygoteInit 启动流程
zygote 本身是一个与启动、内核等无关 Native 的应用程序,它是 Java 世界的开创者。它是由 init 进程在解析 init.rc 文件中的配置创建的。zygote 最初的名字叫 “app_process”,这个名字是在 Android.mk 文件中指定,在运行中,app_process 通过 linux 下的 pctrl 系统调用将自己的名字换成了 zygote
在 Java 中,不同虚拟机实例会为不同的应用分配不同的内存。如果 Android 应用要尽可能快地启动,但如果 Android 系统为每一个应用启动不同的 Dalvik 虚拟机实例,就会消耗大量的内存以及空间。因此,为了克服这个问题,Android 系统创造了 “Zygote”。 zygote 让 Dalvik 虚拟机共享代码、低内存占用以及最小的启动时间成为可能。
zygote 进程由 init 进程 fork 而来, 在init.rc 里面指定启动参数如下:
1 | #init.rc |
zygote 的原型 app_process 对应源文件为:app_main.cpp
,其中入口是main 函数,代码如下:
1 | int main(int argc, const char* const argv[]) |
AppRuntime 分析
AppRuntime 是从 AndroidRuntime 类派生的,AppRuntime 声明和实现在 app_main.cpp 中,重载了 onStarted,onZygoteInit 和 onExit 函数。其中 AndroidRuntime:: start(const char* className, const bool startSystemServer) 函数做了几件事情:
- 创建虚拟机 startVm
- 注册 JNI 函数 startReg
- 通过 JNI 调用 Java 函数,进入 Java 世界
env->CallStaticVoidMethod
代码如下
1 | void AndroidRuntime::start(const char*className, const bool startSystemServer) |
ZygoteInit 分析
根据上面的分析 ZygoteInit 的main 函数最终 由env->CallStaticVoidMethod
调用得到执行的,也就是从这里开始,通过 zygote 进入了 Java 世界。
在 ZygoteInit 的 main 函数中,主要做了以下几件事情:
- 注册 zygote 用的 socket
registerZygoteSocket()
- 预加载类和资源
preloadClasses()、preloadResources()
- 启动 system_sever 进程
- 调用
runSelectLoopMode
函数,进入等待socket 连接和 sokcet 消息处理
代码如下:
1 | public static void main(String argv[]){ |
1、注册socket,建立 IPC 通信服务端—— registerZygoteSocket
Zygote以及系统中其他程序的通信没有使用Binder,而是采用了基于AF_UNIX类型的Socket。registerZygoteSocket函数的使命正是建立这个Socket。代码如下所示:
1 | private static void registerZygoteSocket() { |
2、预加载类和资源
preloadClasses 方法就是根据 preloaded-classes
文件中的预加载类信息去加载类。
preloaded-classes 文件由framework/base/tools/preload工具生成,它需要判断每个类加载的时间是否大于1250微秒,超过这个时间的类就会被写到preload-classes文件中,最后由zygote预加载。
preloadClass函数的执行时间比较长,这是导致Android系统启动慢的原因之一。对这一块可以做一些优化,但优化是基于对整个系统有比较深入了解才能实现的。
preloadResources 和preloadClass类似,它主要是加载framework-res.apk中的资源。
3、启动system_server
startSystemServer。这个函数会创建Java世界中系统Service所驻留的进程system_server,该进程是framework的核心。如果它死了,就会导致zygote自杀。
1 | private static boolean startSystemServer() |
4、 有求必应之等待请求——runSelectLoopMode
当Zygote从startSystemServer返回后,将进入第四个关键函数:runSelectLoopMode。前面,在第一个关键点registerZygoteSocket中注册了一个用于IPC的Socket,不过那时还没有地方用到它。它的用途将在这个runSelectLoopMode中体现出来,请看下面的代码:
1 | private static void runSelectLoopMode() |
runSelectLoopMode比较简单,就是:
- 处理客户连接和客户请求。其中客户在Zygote中用ZygoteConnection对象来表示。
- 客户的请求由ZygoteConnection的runOnce来处理。
zygote 创建Java 世界总结
- 创建 AppRuntime 对象,调用它的 start 方法
- 调用 startVm 创建 Java 虚拟机,然后调用 startReg 来注册 JNI 函数。
- 通过 JNI
CallStaticVoidMethod
调用 com.android.internal.os.ZygoteInit 类的main 函数,进入 Java 世界。 - 调用 registerZygoteSocket 。通过 这个函数,可以响应子孙进程的请求,同时调用 preloadClasses 和 preloadResources 预加载类和系统资源
- 通过 startSystemServer 分裂一个子进程 system_server
- 调用 runSelectLoopMode 进入 等待唤醒
SystemServer 启动流程
由上面的 zygote 启动初始化流程可知, SystemServer 是由 Zygote.forkSystemServer
函数通过系统调用 fork 出来的。
在 SystemServer 进程fork 成功后,便进入 handleSystemServerProcess(parseArgs) 函数,从此便进入 system_server 进程,执行相关初始化工作,并且调用 Java 世界的 com.android.server.SystemServer 。
接下来根据源码做进一步分析:
ZygoteInit.handleSystemServerProcess
1 | private static void handleSystemServerProcess( |
RuntimeInit.java
1 | public static final void zygoteInit(String[]argv) |
SystemServer调用了zygoteInitNative后,将与Binder通信系统建立联系,这样SystemServer就可以使用Binder了。zygoteInitNative中调用了onZygoteInit()
1 | virtual void onZygoteInit(){ |
invokeStaticMain 函数
1 | private static void invokeStaticMain(String className, String[] argv) throws ZygoteInit.MethodAndArgsCaller { |
invokeStaticMain竟然抛出了一个异常,它是在哪里被截获呢?是在ZygoteInit的main函数中。请看这段代码:
注意:我们所在的进程是system_server。
1 | if (argv[1].equals("true")) { |
为什么要主动抛出这样一个异常来执行main函数呢?
《深入理解Android 卷I》中是这样解释的:这个调用是在ZygoteInit.main中,相当于Native的main函数,也即入口函数,位于堆栈的顶层。如果不采用抛异常的方式,而是在invokeStaticMain那调用,则会浪费之前函数调用所占用的一些调用堆栈。
java 世界的 SystemServer
从上面的分析可知,在 invokeStaticMain 方法抛出异常后,然后在 ZygoteInit.main 中捕获异常,然后调用 MethodAndArgsCaller.run 函数,最终通过反射调用到 com.android.server.SystemServer类的main函数
下面将对 SystemServer 流程展开分析
main 函数
1 | public static void main(String[] args) { |
在 main 函数中主要做两件事情:
1、加载 libandroid_servers.so 动态库,这个库所包含的源码文件在文件夹framework/base/services/jni下
2、调用native的init1函数。
init1是native函数,在com_android_server_SystemServer.cpp中实现。
com_android_server_SystemServer.cpp
1 | extern "C" int system_init(); |
system_init的实现在system_init.cpp中,它的代码如下所示:
system_init.cpp
1 | extern "C" status_t system_init() |
init1函数创建了一些系统服务,然后把调用线程加入Binder通信中。不过其间还通过JNI调用了com.android.server.SystemServer类的init2函数,下面就来看看这个init2函数。
init2在Java层,代码在SystemServer.java中,如下所示:
SystemServer.java
1 | public static final void init2() { |
启动了一个ServerThread线程。请直接看它的run函数。
在run 函数中,主要启动 系统线程、AMS、WMS、PMS 等各种service
SystemServer 总结
- ZygoteInit 调用 startSystemServer 创建 system_server 进程
- SystemServer 调用 handleSystemServerProcess 完成自己的使命
- handleSystemServerProcess 抛出异常,最终调用com.android.server.SystemServer的main函数。
- main函数加载libandroid_server.so并调用native的init1函数。
- init1函数通过JNI调用com.android.server.SystemServer类的init2函数。init2函数创建一个线程,用于加载各种service。
- init1函数最终加入到Binder通信系统。
Zygote 的分裂
在 zygote fork 出 system_server 进程后,就通过 runSelectLoopMode
等待来自客户端的消息请求。接下来以启动Hom界面 作为分析案例。
在Application Framework层的ActivityManagerService准备就绪后,就会通知各个模块,继续执行上层应用
ServerThread
1 | ActivityManagerService.self().systemReady(new Runnable() { |
ActivityManagerService.AThread
1 | public void systemReady(final Runnable goingCallback) { |
ActivityStack.resumeTopActivityLocked
1 | final boolean resumeTopActivityLocked(ActivityRecord prev) { |
在SystemServer 启动完成后,然后就启动一个 Home 应用程序,也即 Lanucher 。其中 Lanucher 在启动的过程中,会请求 PackageManagerService 返回已经安装的的应用程序信息,并将这些应用程序封装成一个快捷图标列表显示在系统屏幕上,这样用用便可以通过点击快捷图标来启动相应的应用程序。
总结系统启动流程
启动电源以及系统启用
当按下电源时,引导芯片代码开始从预定义地方(固化ROM)开始执行,加载引导程序 BootLoader 到 RAM,然后执行。
引导程序BootLoader
引导程序 BootLoader 时Android 系统启动前的一个小程序,类似 BIOS,通过引导程序可以将系统引导加载到 RAM中拉起来运行。
Linux 启动内核
Linux 启动后会加载驱动、挂载根目录、计划表等等,但完成系统的设置后,会启动 第一个进程 init 进程,并查找加载 init.rc 文件。
init进程
init 会进行相关初始化运行,并且fork Zygote进程,然后根据 init.rc 运行 app_process, 并携带 “–zygote” 和 “–startSystemServer” 参数。
AndroidRuntime.cpp::start() 里将启动 JavaVM,并且注册所有 framework 相关系统 JNI 接口。
通过 CallStaticVoidMethod 调用 ZygoteInit.java::main() 函数初始化进入到 Java 世界。
Zygote进程启动
创建 JavaVM 并且注册 JNI 接口,创建 服务端 Socket,预加载常用Java 类库,以及系统的 resource 资源,同时GC 清理内存空间,启动 SystemServer,然后进入 selectSocketLoopMode 等待socket 客户端的消息连接和处理。
SystemServer 进程启动
启动 Binder 线程池和 SystemServiceManager,并启动各种系统服务。
Lanucher 启动
被 SystemServer 进程启动的ActivityManagerService 会启动 Launcher, Launcher启动后会将已经安装的应用程序的快捷图标显示到界面上。
资料来源: