当前位置:操作系统 > 安卓/Android >>

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所示:

 \
 


图1 Dalvik虚拟机的运行过程

        这个过程可以分为9个步骤,接下来我们就详细分析每一个步骤。

        Step 1. JNIEnv.CallStaticVoidMethod


[cpp]  struct _JNIEnv; 
...... 
typedef _JNIEnv JNIEnv; 
...... 
 
struct _JNIEnv { 
    /* do not rename this; it does not seem to be entirely opaque */ 
    const struct JNINativeInte易做图ce* functions; 
    ...... 
 
    void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...) 
    { 
        va_list args; 
        va_start(args, methodID); 
        functions->CallStaticVoidMethodV(this, clazz, methodID, args); 
        va_end(args); 
    } 
 
    ...... 
}; 

struct _JNIEnv;
......
typedef _JNIEnv JNIEnv;
......

struct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInte易做图ce* functions;
    ......

    void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
    {
        va_list args;
        va_start(args, methodID);
        functions->CallStaticVoidMethodV(this, clazz, methodID, args);
        va_end(args);
    }

    ......
};        这个函数定义在文件dalvik/libnativehelper/include/nativehelper/jni.h中。

        JNIEnv实际上是一个结构,它有一个成员变量functions,指向的是一个回调函数表。这个回调函数表使用一个JNINativeInte易做图ce对象来描述。JNIEnv结构体的成员函数CallStaticVoidMethod的实现很简单,它只是调用该回调函数表中的CallStaticVoidMethodV函数来执行参数clazz和methodID所描述的Java代码。

        Step 2. JNINativeInte易做图ce.CallStaticVoidMethodV


[cpp]  struct JNINativeInte易做图ce { 
    ...... 
 
    void        (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list); 
 
    ...... 
}; 

struct JNINativeInte易做图ce {
    ......

    void        (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);

    ......
};        这个函数定义在文件dalvik/libnativehelper/include/nativehelper/jni.h中。


        JNINativeInte易做图ce是一个结构体,它的成员变量CallStaticVoidMethodV是一个函数指针。

        从前面Dalvik虚拟机的启动过程分析一文可以知道,Dalvik虚拟机在内部为Zygote进程的主线程所创建的Java环境是用一个JNIEnvExt结构体来描述的,并且这个JNIEnvExt结构体会被强制转换成一个JNIEnv结构体返回给Zygote进程。

        JNIEnvExt结构体定义在文件dalvik/vm/JniInternal.h中,如下所示:


[cpp]  typedef struct JNIEnvExt { 
    const struct JNINativeInte易做图ce* funcTable;     /* must be first */ 
 
    ...... 
} JNIEnvExt; 

typedef struct JNIEnvExt {
    const struct JNINativeInte易做图ce* funcTable;     /* must be first */

    ......
} JNIEnvExt;        从这里就可以看出,虽然结构体JNIEnvExt和JNIEnv之间没有继承关系,但是它们的第一个成员变量的类型是一致的,也就是它们都是指向一个类型为JNINativeInte易做图ce的回调函数表,因此,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指向全局变量gNativeInte易做图ce所描述的一个回调函数表。

        gNativeInte易做图ce定义在文件dalvik/vm/Jni.c中,如下所示:


[cpp]  static const struct JNINativeInte易做图ce gNativeInte易做图ce = { 
    ...... 
 
    CallStaticVoidMethodV, 
 
    ...... 
}; 

static const struct JNINativeInte易做图ce gNativeInte易做图ce = {
    ......

    CallStaticVoidMethodV,

    ......
};       在这个回调函数表中,名称为CallStaticVoidMethodV的函数指针指向的是一个同名函数CallStaticVoidMethodV。

       函数CallStaticVoidMethodV同样是定义在文件dalvik/vm/Jni.c中,不过它是通过宏CALL_STATIC来定义的,如下所示:


[cpp]  #define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref)               \  
    ......                                                                  \ 
    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz,   \ 
        jmethodID methodID, va_list args)   &nb

补充:移动开发 , Android ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,