您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 其它行业文档 > 2Dalvik虚拟机的运行过程分析
Dalvik虚拟机的运行过程分析分类:Android2013-05-2000:5723620人阅读评论(17)收藏举报AndroidDalvikRun运行在前面一篇文章中,我们分析了Dalvik虚拟机在Zygote进程中的启动过程。Dalvik虚拟机启动完成之后,也就是在各个子模块初始化完成以及加载了相应的Java核心类库之后,就是可以执行Java代码了。当然,Dalvik虚拟机除了可以执行Java代码之外,还可以执行Native代码,也就是C和C++代码。在本文中,我们就将继续以Zygote进程的启动过程为例,来分析Dalvik虚拟机的运行过程。从前面Dalvik虚拟机的启动过程分析一文可以知道,Dalvik虚拟机在Zygote进程中启动完成之后,就会获得一个JavaVM实例和一个JNIEnv实例。其中,获得的JavaVM实例就是用来描述Zygote进程的Dalvik虚拟机实例,而获得的JNIEnv实例描述的是Zygote进程的主线程的JNI环境。紧接着,Zygote进程就会通过前面获得的JNIEnv实例的成员函数CallStaticVoidMethod来调用com.android.internal.os.ZygoteInit类的静态成员函数main。这就相当于是将com.android.internal.os.ZygoteInit类的静态成员函数main作为Java代码的入口点。接下来,我们就从JNIEnv类的成员函数CallStaticVoidMethod开始,分析Dalvik虚拟机的运行过程,如图1所示:图1Dalvik虚拟机的运行过程这个过程可以分为9个步骤,接下来我们就详细分析每一个步骤。Step1.JNIEnv.CallStaticVoidMethod[cpp]viewplaincopyprint?1.struct_JNIEnv;2.......3.typedef_JNIEnvJNIEnv;4.......5.6.struct_JNIEnv{7./*donotrenamethis;itdoesnotseemtobeentirelyopaque*/8.conststructJNINativeInterface*functions;9.......10.11.voidCallStaticVoidMethod(jclassclazz,jmethodIDmethodID,...)12.{13.va_listargs;14.va_start(args,methodID);15.functions-CallStaticVoidMethodV(this,clazz,methodID,args);16.va_end(args);17.}18.19.......20.};这个函数定义在文件dalvik/libnativehelper/include/nativehelper/jni.h中。JNIEnv实际上是一个结构,它有一个成员变量functions,指向的是一个回调函数表。这个回调函数表使用一个JNINativeInterface对象来描述。JNIEnv结构体的成员函数CallStaticVoidMethod的实现很简单,它只是调用该回调函数表中的CallStaticVoidMethodV函数来执行参数clazz和methodID所描述的Java代码。Step2.JNINativeInterface.CallStaticVoidMethodV[cpp]viewplaincopyprint?1.structJNINativeInterface{2.......3.4.void(*CallStaticVoidMethodV)(JNIEnv*,jclass,jmethodID,va_list);5.6.......7.};这个函数定义在文件dalvik/libnativehelper/include/nativehelper/jni.h中。JNINativeInterface是一个结构体,它的成员变量CallStaticVoidMethodV是一个函数指针。从前面Dalvik虚拟机的启动过程分析一文可以知道,Dalvik虚拟机在内部为Zygote进程的主线程所创建的Java环境是用一个JNIEnvExt结构体来描述的,并且这个JNIEnvExt结构体会被强制转换成一个JNIEnv结构体返回给Zygote进程。JNIEnvExt结构体定义在文件dalvik/vm/JniInternal.h中,如下所示:[cpp]viewplaincopyprint?1.typedefstructJNIEnvExt{2.conststructJNINativeInterface*funcTable;/*mustbefirst*/3.4.......5.}JNIEnvExt;从这里就可以看出,虽然结构体JNIEnvExt和JNIEnv之间没有继承关系,但是它们的第一个成员变量的类型是一致的,也就是它们都是指向一个类型为JNINativeInterface的回调函数表,因此,Dalvik虚拟机可以将一个JNIEnvExt结构体强制转换成一个JNIEnv结构体返回给Zygote进程,这时候我们通过JNIEnv结构体来访问其成员变量functions所描述的回调函数表时,实际访问到的是对应的JNIEnvExt结构体的成员变量funcTable所描述的回调函数表。为什么不直接让JNIEnvExt结构体从JNIEnv结构体继承下来呢?这样把一个JNIEnvExt结构体转换为一个JNIEnv结构体就是相当直观的。然而,Dalvik虚拟机的源代码并一定是要以C++语言的形式来编译的,它也可以以C语言的形式来编译的。由于C语言没有继承的概念,因此,为了使得Dalvik虚拟机的源代码能同时兼容C++和C,这里就使用了一个Trick:只要两个结构体的内存布局相同,它们就可以相互转换访问。当然,这并不要求两个结构体的内存布局完全相同,但是至少开始部分要求是相同的。在这种情况下,将一个结构体强制转换成另外一个结构体之外,只要不去访问内存布局不一致的地方,就没有问题。在Android系统的Native代码中,我们可以常常看到这种Trick。接下来,我们需要搞清楚的是JNIEnvExt结构体的成员变量funcTable指向的回调函数表是什么。同样是从前面Dalvik虚拟机的启动过程分析一文可以知道,Dalvik虚拟机在创建一个JNIEnvExt结构体的时候,会将它的成员变量funcTable指向全局变量gNativeInterface所描述的一个回调函数表。gNativeInterface定义在文件dalvik/vm/Jni.c中,如下所示:[cpp]viewplaincopyprint?1.staticconststructJNINativeInterfacegNativeInterface={2.......3.4.CallStaticVoidMethodV,5.6.......7.};在这个回调函数表中,名称为CallStaticVoidMethodV的函数指针指向的是一个同名函数CallStaticVoidMethodV。函数CallStaticVoidMethodV同样是定义在文件dalvik/vm/Jni.c中,不过它是通过宏CALL_STATIC来定义的,如下所示:[cpp]viewplaincopyprint?1.#defineCALL_STATIC(_ctype,_jname,_retfail,_retok,_isref)\2.......\3.static_ctypeCallStatic##_jname##MethodV(JNIEnv*env,jclassjclazz,\4.jmethodIDmethodID,va_listargs)\5.{\6.UNUSED_PARAMETER(jclazz);\7.JNI_ENTER();\8.JValueresult;\9.dvmCallMethodV(_self,(Method*)methodID,NULL,true,&result,args);\10.if(_isref&&!dvmCheckException(_self))\11.result.l=addLocalReference(env,result.l);\12.JNI_EXIT();\13.return_retok;\14.}15.......\16.CALL_STATIC(void,Void,,,false);通过上面的分析就可以知道,在JNIEnvExt结构体的成员变量funcTable所描述的回调函数表中,名称为CallStaticVoidMethodV的函数指针指向的是一个同名函数CallStaticVoidMethodV。这就是说,我们通过JNIEnv结构体的成员变量functions所访问到的名称为CallStaticVoidMethodV函数指针实际指向的是函数CallStaticVoidMethodV。Step3.CallStaticVoidMethodV我们将上面的CALL_STATIC宏开之后,就可以得到函数CallStaticVoidMethodV的实现,如下所示:[cpp]viewplaincopyprint?1.static_ctypeCallStaticVoidMethodV(JNIEnv*env,jclassjclazz,2.jmethodIDmethodID,va_listargs)3.{4.UNUSED_PARAMETER(jclazz);5.JNI_ENTER();6.JValueresult;7.dvmCallMethodV(_self,(Method*)methodID,NULL,true,&result,args);8.if(_isref&&!dvmCheckException(_self))9.result.l=addLocalReference(env,result.l);10.JNI_EXIT();11.return_retok;12.}函数CallStaticVoidMethodV的实现很简单,它通过调用另外一个函数dvmCallMethodV来执行由参数jclazz和methodID所描述的Java代码,因此,接下来我们就继续分析函数dvmCallMethodV的实现。Step4.dvmCallMethodV[cpp]viewplaincopyprint?1.voiddvmCallMethodV(Thread*self,constMethod*method,Object*obj,2.boolfromJni,JValue*pResult,va_listargs)3.{4.......5.6.if(dvmIsNativeMethod(method)){7.TRACE_METHOD_ENTER(self,method);8./*9.*Becauseweleavenospaceforlocalvariables,curFramepoints10.*directlyatthemethodarguments.11.*/12.(*method-nativeFunc)(self-curFrame,pResult,method,self);13.TRACE_METHOD_EXIT(self,method);14.}else{15.dvmInterpret(self,method,pResult);16.}17.18.......19.}这个函数定义在文件dalvik/vm/interp/Stack.c中。函数dvmCallMethodV首先检查参数method描述的函数是否是一个JNI方法。如果是的话,那么它所指向的一个Method对象的成员变量nativeFunc就指向该JNI方法的地址,因此就可以直接对它进行调用。否则的话,就说明参数method描述的是一个Java函数,这时候就需要继续调用函数dv
本文标题:2Dalvik虚拟机的运行过程分析
链接地址:https://www.777doc.com/doc-2914150 .html