Dalvik虚拟机启动全程解析

本文试图记录Dalvik虚拟机启动时全过程。

0x01:启动点

我们知道,Android基于linux,linux的第一个进程为init,它启动别的进程,在Android中,它启动了zygote:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

从此, 代码从linux的世界进入到了Android的世界。

在上面可以看到,启动的脚本是/system/bin/app_process,在后面的分析中,我们发现,这个就是Dalvik虚拟机的启动点。app_process除了在init.rc中启动,也可以通过命令执行,比如am命令就是调用了app_process:

#!/system/bin/sh
#
# Script to start "am" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/am.jar
exec app_process $base/bin com.android.commands.am.Am "$@"

0x02:app_process

既然确定了启动点,我们就来跟踪一下,为了简单,我们就分析init.rc的调用场景。

app_process的源码是app_main.cpp,其main函数主要做了几个事情:

  1. 参数解析,通过源码,我们可以看到,启动虚拟机可以有很多很多的参数
  2. 设置相关环境变量
  3. 调用runtime.start,这个是真正启动虚拟机的地方,其参数为:com.android.internal.os.ZygoteInit,表明在启动虚拟机后,开始执行ZygoteInit来做Zygote的初始化。对于Zygote的初始化及后续的事情,本文不关心,我们还是集中于虚拟机的启动。

0x03:runtime.start

源码见AndroidRuntime.cpp

AndroidRuntime::start中,主要做了几件事:

  1. startVm,启动虚拟机
  2. onVmCreated 执行启动后的回调
  3. startReg,注册Andorid系统相关的native函数
  4. 调用参数里面指定的类的main

重点是startvmstartReg,继续跟踪。

0x04:startVm

源码同样在AndroidRuntime.cpp

AndroidRuntime::startVm中,主要做了几件事:

  1. 大段的参数解析,解析到了JavaVMInitArgs
  2. JNI_CreateJavaVM,函数里面需要设置pJavaVM, pEnv指针,是为了后续的销毁虚拟机等操作。

0x05:JNI_CreateJavaVM

源码见Jni.cpp

JNI_CreateJavaVM中,主要做了几件事情:

  1. 根据参数,填充了gDvm中的相关参数
    关于gDvm:它的定义在Globals.h,其作用是保存许许多多的全局变量,在整个虚拟机中使用。
  2. 初始化了JavaVMExt、JNIEnvExt等指针,这些分别对应的JNI中的JavaVM、JNIEnv,在JNI调用中必不可少。
  3. dvmStartup 继续启动,重点是这个

0x06:dvmStartup

源码见Init.cpp

dvmStartup中,主要做了几件事:

  1. 继续解析参数,填充gDvm中的相关参数,参数是真多啊,说明虚拟机的可配置项还是挺多的。
  2. dvmCheckAsmConstants,检查mterp interpreter用到的若干常量
  3. dvmQuasiAtomicsStartup, 初始化原子操作的mutex
  4. dvmGcStartup,初始化metex,初始化GC堆区
  5. dvmThreadStartup,初始化线程相关的环境
  6. dvmInlineNativeStartup,分配InlineNative相关的内存,将gDvm.inlinedMethods指向这块内存,所谓InlineNative就是指一些Java方法,使用了效率更高的C函数实现,执行这些方法时,直接调用到这些C函数,不用在执行字节码,和inLine有异曲同工之妙。
  7. dvmRegisterMapStartup,分配MapStats的内存,将gDvm.registerMapStats指向这块内存,MapStats应该是和RegisterMap相关。
  8. bool dvmInstanceofStartup(), 分配instanceofCache的内存,将gDvm.instanceofCache指向这块内存,目测这块内存会在instanceof操作时使用。
  9. dvmClassStartup,比较复杂,分别说:
    9.1. 创建了一个Hash表,用来存放所有加载的类,将gDvm.loadedClasses指向这个Hash表
    9.2. 分配InitiatingLoaderList的内存,将gDvm.initiatingLoaderList指向这块内存,InitiatingLoaderList在寻找加载的类的时候会用到
    9.3. createInitialClasses,构建Ljava/lang/Class;的类对象,和所有的原类型的类对象:void,boolean,byte,short,char,int,long,float,double,至此,我们就可以使用这些类了。
    9.4. processClassPath,加载bootClassPath下的所有的jar(其实是dex)到gDvm.loadedClasses中,bootClassPath为:
    Dalvik虚拟机启动全程解析_第1张图片

    至此,Android framework相关的类也被加载了。
  10. dvmFindRequiredClassesAndMembers,主要是将相关常用到的类、属性、构造器、方法等,直接关联到gDvm的相关指针上,省得每次都要去Hash表里面查。
  11. dvmStringInternStartup,创建了internedStrings和literalStrings的Hash表,作用暂时不明。
  12. dvmNativeStartup,创建用于保存sharedLib(也就是so)的Hash表,将gDvm.nativeLibs指向此表。以后每个加载的so的相关信息都会被保存到这个表中,其中一个作用当然是避免重复加载。
  13. dvmInternalNativeStartup, 遍历很多类,设置这些类的classDescriptorHash,这些类如下:
    Dalvik虚拟机启动全程解析_第2张图片

    这些类来自于libcore中,对应的c实现在dalvik下。
  14. dvmJniStartup,初始化了jni的全局引用表和全局弱引用表。(关于JNI,以后单独写一篇文章)
  15. dvmProfilingStartup, 貌似和跟踪功能相关的初始化
  16. dvmCreateInlineSubsTable,这里才将InlineNative类的Native方法和c实现建立了关联表,将gDvm.inlineSubs指向关联表。
  17. dvmValidateBoxClasses,校验盒类对象,简单校验对象的属性只有一个:即value的定义。
  18. dvmPrepMainForJni,创建了一个假的栈帧,将JNIEnv设置到当前的Thread中
  19. dvmInitClass(gDvm.classJavaLangClass),初始化Class类对象
  20. registerSystemNatives, 将Class中的getDex注册到JNI中,加载libjavacore.so和libnativehelper.so
  21. dvmCreateStockExceptions, 创建一些固定的Exception对象,并关联到gDvm
  22. dvmPrepMainThread, 创建相关的线程对象,将当前线程命令为main线程
  23. dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0 简单校验线程相关的local引用表当前是空的
  24. dvmDebuggerStartup, 初始化调试相关的东西,比如断点什么的
  25. dvmGcStartupClasses, 调用Ljava/lang/Daemons;的start方法,启动三个和GC相关的线程:
    public static void start() {
        ReferenceQueueDaemon.INSTANCE.start();
        FinalizerDaemon.INSTANCE.start();
        FinalizerWatchdogDaemon.INSTANCE.start();
    }
  1. initZygote,挂在相关的文件系统,设置进程属性等。
  2. 最后dvmCheckException,检查是否有错。

至此dvmStartup分析完毕。总结一下就是,分配相关内存,建立相关的联系,初始化相关环境,启动相关的线程。(相关这个词真是好用)

此时执行完0x03:runtime.start中中的第一步:startVm,第二步是一个回调,实现为空,继续分析第三步:startReg

0x07: startReg

源码见AndroidRuntime.cpp

AndroidRuntime::startReg中,主要做了几件事:

  1. register_jni_procs,将Android framework中所有的native方法注册到JNI中,这些类在framework.jar和framework2.jar中,c逻辑在libandroid_runtime.so中

至此,虚拟机的启动完成了,之后就会执行com.android.internal.os.ZygoteInitmain方法,开始在虚拟机中对Zygote进行初始化操作了。

Dalvik虚拟机启动全程解析_第3张图片

你可能感兴趣的:(Dalvik虚拟机启动全程解析)