前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Dalvik虚拟机启动全程解析0x07: startReg

Dalvik虚拟机启动全程解析0x07: startReg

作者头像
用户2930595
发布2018-08-23 09:46:06
8560
发布2018-08-23 09:46:06
举报

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

0x01:启动点

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

代码语言:javascript
复制
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:

代码语言:javascript
复制
#!/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为:

至此,Android framework相关的类也被加载了。

  1. dvmFindRequiredClassesAndMembers,主要是将相关常用到的类、属性、构造器、方法等,直接关联到gDvm的相关指针上,省得每次都要去Hash表里面查。
  2. dvmStringInternStartup,创建了internedStrings和literalStrings的Hash表,作用暂时不明。
  3. dvmNativeStartup,创建用于保存sharedLib(也就是so)的Hash表,将gDvm.nativeLibs指向此表。以后每个加载的so的相关信息都会被保存到这个表中,其中一个作用当然是避免重复加载。
  4. dvmInternalNativeStartup, 遍历很多类,设置这些类的classDescriptorHash,这些类如下:

这些类来自于libcore中,对应的c实现在dalvik下。

  1. dvmJniStartup,初始化了jni的全局引用表和全局弱引用表。(关于JNI,以后单独写一篇文章)
  2. dvmProfilingStartup, 貌似和跟踪功能相关的初始化
  3. dvmCreateInlineSubsTable,这里才将InlineNative类的Native方法和c实现建立了关联表,将gDvm.inlineSubs指向关联表。
  4. dvmValidateBoxClasses,校验盒类对象,简单校验对象的属性只有一个:即value的定义。
  5. dvmPrepMainForJni,创建了一个假的栈帧,将JNIEnv设置到当前的Thread中
  6. dvmInitClass(gDvm.classJavaLangClass),初始化Class类对象
  7. registerSystemNatives, 将Class中的getDex注册到JNI中,加载libjavacore.so和libnativehelper.so
  8. dvmCreateStockExceptions, 创建一些固定的Exception对象,并关联到gDvm
  9. dvmPrepMainThread, 创建相关的线程对象,将当前线程命令为main线程
  10. dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0 简单校验线程相关的local引用表当前是空的
  11. dvmDebuggerStartup, 初始化调试相关的东西,比如断点什么的
  12. dvmGcStartupClasses, 调用Ljava/lang/Daemons;的start方法,启动三个和GC相关的线程:
代码语言:javascript
复制
    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进行初始化操作了。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.06.15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01:启动点
  • 0x02:app_process
  • 0x03:runtime.start
  • 0x04:startVm
  • 0x05:JNI_CreateJavaVM
  • 0x06:dvmStartup
  • 0x07: startReg
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档