本文试图记录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函数主要做了几个事情:
- 参数解析,通过源码,我们可以看到,启动虚拟机可以有很多很多的参数
- 设置相关环境变量
- 调用
runtime.start
,这个是真正启动虚拟机的地方,其参数为:com.android.internal.os.ZygoteInit
,表明在启动虚拟机后,开始执行ZygoteInit来做Zygote的初始化。对于Zygote的初始化及后续的事情,本文不关心,我们还是集中于虚拟机的启动。
0x03:runtime.start
在AndroidRuntime::start
中,主要做了几件事:
-
startVm
,启动虚拟机 -
onVmCreated
执行启动后的回调 -
startReg
,注册Andorid系统相关的native函数 - 调用参数里面指定的类的main
重点是startvm
和startReg
,继续跟踪。
0x04:startVm
源码同样在AndroidRuntime.cpp
在AndroidRuntime::startVm
中,主要做了几件事:
- 大段的参数解析,解析到了
JavaVMInitArgs
中 -
JNI_CreateJavaVM
,函数里面需要设置pJavaVM, pEnv指针,是为了后续的销毁虚拟机等操作。
0x05:JNI_CreateJavaVM
源码见Jni.cpp
在JNI_CreateJavaVM
中,主要做了几件事情:
- 根据参数,填充了
gDvm
中的相关参数
关于gDvm
:它的定义在Globals.h,其作用是保存许许多多的全局变量,在整个虚拟机中使用。 - 初始化了JavaVMExt、JNIEnvExt等指针,这些分别对应的JNI中的JavaVM、JNIEnv,在JNI调用中必不可少。
- dvmStartup 继续启动,重点是这个
0x06:dvmStartup
源码见Init.cpp
在dvmStartup
中,主要做了几件事:
- 继续解析参数,填充
gDvm
中的相关参数,参数是真多啊,说明虚拟机的可配置项还是挺多的。 -
dvmCheckAsmConstants
,检查mterp interpreter用到的若干常量 -
dvmQuasiAtomicsStartup
, 初始化原子操作的mutex -
dvmGcStartup
,初始化metex,初始化GC堆区 -
dvmThreadStartup
,初始化线程相关的环境 -
dvmInlineNativeStartup
,分配InlineNative相关的内存,将gDvm.inlinedMethods
指向这块内存,所谓InlineNative
就是指一些Java方法,使用了效率更高的C函数实现,执行这些方法时,直接调用到这些C函数,不用在执行字节码,和inLine有异曲同工之妙。 -
dvmRegisterMapStartup
,分配MapStats的内存,将gDvm.registerMapStats
指向这块内存,MapStats应该是和RegisterMap
相关。 -
bool dvmInstanceofStartup()
, 分配instanceofCache的内存,将gDvm.instanceofCache
指向这块内存,目测这块内存会在instanceof
操作时使用。 -
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相关的类也被加载了。 -
dvmFindRequiredClassesAndMembers
,主要是将相关常用到的类、属性、构造器、方法等,直接关联到gDvm
的相关指针上,省得每次都要去Hash表里面查。 -
dvmStringInternStartup
,创建了internedStrings和literalStrings的Hash表,作用暂时不明。 -
dvmNativeStartup
,创建用于保存sharedLib(也就是so)的Hash表,将gDvm.nativeLibs
指向此表。以后每个加载的so的相关信息都会被保存到这个表中,其中一个作用当然是避免重复加载。 -
dvmInternalNativeStartup
, 遍历很多类,设置这些类的classDescriptorHash,这些类如下:
这些类来自于libcore中,对应的c实现在dalvik下。 -
dvmJniStartup
,初始化了jni的全局引用表和全局弱引用表。(关于JNI,以后单独写一篇文章) -
dvmProfilingStartup
, 貌似和跟踪功能相关的初始化 -
dvmCreateInlineSubsTable
,这里才将InlineNative类的Native方法和c实现建立了关联表,将gDvm.inlineSubs
指向关联表。 -
dvmValidateBoxClasses
,校验盒类对象,简单校验对象的属性只有一个:即value的定义。 -
dvmPrepMainForJni
,创建了一个假的栈帧,将JNIEnv设置到当前的Thread中 -
dvmInitClass(gDvm.classJavaLangClass)
,初始化Class类对象 -
registerSystemNatives
, 将Class中的getDex注册到JNI中,加载libjavacore.so和libnativehelper.so -
dvmCreateStockExceptions
, 创建一些固定的Exception对象,并关联到gDvm
上 -
dvmPrepMainThread
, 创建相关的线程对象,将当前线程命令为main
线程 -
dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0
简单校验线程相关的local引用表当前是空的 -
dvmDebuggerStartup
, 初始化调试相关的东西,比如断点什么的 -
dvmGcStartupClasses
, 调用Ljava/lang/Daemons;的start方法,启动三个和GC相关的线程:
public static void start() {
ReferenceQueueDaemon.INSTANCE.start();
FinalizerDaemon.INSTANCE.start();
FinalizerWatchdogDaemon.INSTANCE.start();
}
-
initZygote
,挂在相关的文件系统,设置进程属性等。 - 最后
dvmCheckException
,检查是否有错。
至此dvmStartup
分析完毕。总结一下就是,分配相关内存,建立相关的联系,初始化相关环境,启动相关的线程。(相关这个词真是好用)
此时执行完0x03:runtime.start中中的第一步:startVm
,第二步是一个回调,实现为空,继续分析第三步:startReg
0x07: startReg
在AndroidRuntime::startReg
中,主要做了几件事:
-
register_jni_procs
,将Android framework中所有的native方法注册到JNI中,这些类在framework.jar和framework2.jar中,c逻辑在libandroid_runtime.so中
至此,虚拟机的启动完成了,之后就会执行com.android.internal.os.ZygoteInit
的main
方法,开始在虚拟机中对Zygote进行初始化操作了。
网友评论