Dalvik虚拟机的运行过程分析

分享一下我老师大神的人工智能教程。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

               

        在前面一篇文章中,我们分析了Dalvik虚拟机在Zygote进程中的启动过程。Dalvik虚拟机启动完成之后,也就是在各个子模块初始化完成以及加载了相应的Java核心类库之后,就是可以执行Java代码了。当然,Dalvik虚拟机除了可以执行Java代码之外,还可以执行Native代码,也就是C和C++代码。在本文中,我们就将继续以Zygote进程的启动过程为例,来分析Dalvik虚拟机的运行过程。 

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

        从前面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

struct _JNIEnv;......typedef _JNIEnv JNIEnv;......struct _JNIEnv {
        /* do not rename this; it does not seem to be entirely opaque */    const struct JNINativeInterface* 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,指向的是一个回调函数表。这个回调函数表使用一个JNINativeInterface对象来描述。JNIEnv结构体的成员函数CallStaticVoidMethod的实现很简单,它只是调用该回调函数表中的CallStaticVoidMethodV函数来执行参数clazz和methodID所描述的Java代码。

        Step 2. JNINativeInterface.CallStaticVoidMethodV

struct JNINativeInterface {
        ......    void        (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);    ......};
        这个函数定义在文件dalvik/libnativehelper/include/nativehelper/jni.h中。

        JNINativeInterface是一个结构体,它的成员变量CallStaticVoidMethodV是一个函数指针。

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

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

typedef struct JNIEnvExt {
        const struct JNINativeInterface* funcTable;     /* must be first */    ......} 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中,如下所示:

static const struct JNINativeInterface gNativeInterface = {
        ......    CallStaticVoidMethodV,    ......};
       在这个回调函数表中,名称为CallStaticVoidMethodV的函数指针指向的是一个同名函数CallStaticVoidMethodV。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值