Dalvik VM的启动过程解析

Dalvik 虚拟机的启动要从android另一个著名的模块,Zygote,说起。

 

我们知道android系统中,每一个java应用被设计成可以运行在一个单独的Linux进程中。而每个该进程都包含一个运行中的dalvik虚拟机实例,用来执行该应用中的java字节码。很多java基础类(比如,java.lang.*)和一些系统级的共享性资源(drawable/color...)等几乎会被所有的进程使用。如果每次虚拟机启动后都去加载这些资源,必然是个很大浪费。其实每个虚拟机里加载这些资源的过程都是一模一样的。Android系统就使用一个巧妙的方式解决这个问题。那就是Zygote的用处。

 

由于本系列文章的重点不是Zygote,而且Zygote的实现也很简单,对于它的介绍网上已经很多了,本文不准备详细介绍Zygote。而是简单的说明一下它和Dalvik相连的部分。


Zygote其实是一个系统级本地服务,在init.rc里定义(如下所示),在系统启动早期被启动起来。它原始名字是app_process(源代码frameworks/base/cmds/app_process/app_main.cpp):

[java]  view plain copy print ?
  1. // @ system/core/rootdir/int.rc  
[java]  view plain copy print ?
  1. service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server  
  2.     class main  
  3.     socket zygote stream 666  
  4.     onrestart write /sys/android_power/request_state wake  
  5.     onrestart write /sys/power/state on  
  6.     onrestart restart media  
  7.     onrestart restart netd  


该服务会在启动后加载前文提到基础类和资源,并且建立socket(名称是如上所示的zygote)。后面其他的java应用程序启动后,AMS(ActivityManagerService,android中用来管理activity的java服务)会通过该管道请求Zygote fock()一个子进程。改子进程继承zygote的内存区,从而直接用于zygote加载的类和资源。用这种方法,运行java应用的linux进程就避免了重复加载android基础类和通用资源,从而极大的节省了时间开销。

Dalvik vm 参考资料一文里引用 【3】有对该过程形象的描述:

Dalvik VM的启动过程解析_第1张图片

我们看到,所有应用(Maps, Browser, Home)都从 Zygote父进程那里继承了共享的库。。

 

从以上描述可以猜测,dalvik启动必然和zygote实现有关联。的确是,dalvik虚拟机实例启动是zygote一手导致的。 本文将向你详细展现这一过程。 但是,本文不打算直接用前面提到的顺序跟踪的方法展现从Zygote启动dalvik虚拟机实例的过程。相反,我们先采用反向追溯的方法。目的如下 1. 介绍反向追溯的使用 2. 加深对整个过程的印象。


从dalvik虚拟机实现里(dalvik/vm)我们看到有两个文件Init.h 和Init.cpp,从命名可以猜测出该文件很可能和虚拟机初始化有关系。初始化话肯定是指改启动时完成的。打开init.h,我们看到如下函数:

Dalvik VM的启动过程解析_第2张图片


从命名来看,这很可能就是dalvik虚拟机启动和关闭的函数。 到Init.cpp里我们可以看到他们的实现。虽然现在还不能完全看懂实现的细节,但可以肯定我们刚才对这两个函数的判断。

好,我们找到了虚拟机初始化的启动函数 dvmStartup()。由此,我们拥有了使用反向追溯的先决条件(一个后期的执行点)。根据这个点,我们就可以往前追溯出虚拟机是如何被启动的。 下面是简单的过程描述:

1. 在代码中全文搜索 改函数 dvmStartup()的使用。很幸运,只有一处真正的调用:

dalvik/vm/Jni.cpp                    3494 dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);


那么启动函数必然是从jni.cpp里调用的,这也和改函数的注释相符(“usually invoked through JNI”)

 

2. 到Jni.cpp里看到,对dvmStartup()调用是在函数JNI_CreateJavaVM()里完成的。继续往前搜索该函数的调用。 过滤掉该函数的声明和定义,我们看到如下几处调用:

frameworks/base/services/surfaceflinger/DdmConnection.cpp 

43 if (JNI_CreateJavaVM(&vm, &env, &args) == 0) {

 

dalvik/dalvikvm/Main.cpp 

212 if (JNI_CreateJavaVM(&vm, &env, &initArgs) < 0) {

 

frameworks/base/core/jni/AndroidRuntime.cpp 

768 if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
769 LOGE("JNI_CreateJavaVM failed\n");


我们知道surfaceflinger是android中用于底层合成图层的本地服务,不大可能作为所有虚拟机实例启动的起点。 另外两个却很有可能。

在前文 xxx (TODO),中我们提到, dalvik实现部署包含两个部分,dalvikvm可执行程序 和 libdvm共享库。这个两个调用正是对应了两种调用启动dalvik虚拟机的方式的入口。可执行程序 dalvikvm在main.cpp中执行JNI_CreateJavaVM启动虚拟机实例,而其他的模块则通过 AndroidRuntime.cpp来启动虚拟机实例。

dalvikvm可执行程序我们在单独的章节介绍,这里我们来接续追溯普通情况下,逻辑是如何执行到AndroidRuntime.cpp里启动 虚拟机实例的。

 

3. 进入AndroidRuntime.cpp我们看到,它是在 AndroidRuntime::startVm()里调用的。继续搜索该函数的调用,只有一个,并且是同一文件里:


frameworks/base/core/jni/AndroidRuntime.cpp  464 int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) function in class:android::AndroidRuntime 
832 if (startVm(&mJavaVM, &env) != 0) {


 

 调用它的函数如下所示:

Dalvik VM的启动过程解析_第3张图片

从该函数的注释可以看出我们追踪的方向是正确的。

 

4. 继续往前,在全文搜索start时,我们得到巨量的搜索结果。毕竟这个函数名字太常用了。 搜索AndroidRuntime::start却只有它的定义,这也难怪,该方法不是个类方法,使用时不会用AndroidRuntime::start(xxx)调用的。

那只能先搜索AndroidRuntime 和 start() 同时出现的使用类,并且只搜索必须有 start。这样子一下子是搜索结果减少到小于十分之一,并且很容易判断出是不是start函数调用。在结果中,我们一眼就看到了它:

 

frameworks/base/cmds/app_process/app_main.cpp 

15 #include <android_runtime/AndroidRuntime.h>
25 "Usage: app_process [java-options] cmd-dir start-class-name [options]\n");
28 class AppRuntime : public AndroidRuntime
68 * The easiest fix is to call FindClass here, early on before we start
88 AndroidRuntime* ar = AndroidRuntime::getRuntime();
108 AndroidRuntime::onExit(code);
169 } else if (strcmp(arg, "--start-system-server") == 0) {
189 runtime.start("com.android.internal.os.ZygoteInit",
190 startSystemServer ? "start

 这个文件名字是不是很眼熟? 是的,前面我们讲过,Zygote实现的源代码起点正是这个文件。

 

5. 展开改文件对start的调用,发现是AppRuntime实例在调用改函数,不是AndroidRuntime! 找到AppRuntime实现,发现它是AndroidRuntime的子类,并且没有它并没有覆盖start()方法,那么它必然是调用了AndroidRuntime的start()方法了。 至此我们已经追溯到Zygote是如何启动Dalvik虚拟机实例的。

出于好奇,我们再看看,start启动的类"com.android.internal.os.ZygoteInit"到底做什么。它的源码在 frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 里面。我们从它的视线里看到了很多和前面介绍的Zygote与DVM交互的实现。比如注册socket,加载基础类以及资源等。

 

至此我们完全可以顺序的勾勒出DVM实例启动的全过程了:

Dalvik VM的启动过程解析_第4张图片

你可能感兴趣的:(java,虚拟机,android,socket,jni,browser)