【分析】dalvik虚拟机启动过程(一)

源码版本:Android-4.4.4_r2

提示:大部分分析直接注释在代码内。



主要函数的调用层次:

|AndroidRuntime::start
    |AndroidRuntime::startVm
        |JNI_CreateJavaVM
            |dvmCreateJNIEnv
            |dvmStartup
                |dvmThreadStartup
                    |pthread_key_create
                    |dvmAllocBitVector
                    |allocThread
                    |prepareThread
                        |assignThreadId
                            |dvmAllocBit
                        |setThreadSelf
    |AndroidRuntime::startReg
        |androidSetCreateThreadFunc
        |register_jni_procs

  • AndroidRuntime::start

    路径:frameworks/base/core/jni/AndroidRuntime.cpp

/**
 * 启动 Android 运行时。这个函数参与虚拟机的启动和
 * 调用 "className" 指定的Java类中的 "static void main(String[] args)" 方法。
 *
 * Passes the main function two arguments, the class name and the specified
 * options string.
 *
 * 当启动zygote时,framework/base/cmds/app_process/app_main.cpp中的的main函数会传入下面的参数:
 * className:"com.android.internal.os.ZygoteInit"
 * options:如果启动systemserver则会传入"start-system-server",否则将会传入一个空字符串。
 */
void AndroidRuntime::start(const char* className, const char* options)
{
    ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
            className != NULL ? className : "(unknown)");

    /*
     * 'startSystemServer == true' means runtime is obsolete and not run from
     * init.rc anymore, so we print out the boot start event here.
     */
    if (strcmp(options, "start-system-server") == 0) {
        /* track our progress through the boot sequence */
        const int LOG_BOOT_PROGRESS_START = 3000;
        LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
                       ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
    }

    // 获得/设置环境变量ANDROID_ROOT
    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /android does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);

    /* 启动虚拟机。 */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }
    // 这是个空函数。
    onVmCreated(env);

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;
    jstring optionsStr;

    // String[] strArray = new String[2];
    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(2, stringClass, NULL);
    assert(strArray != NULL);
    // strArray[0] = classNameStr;
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);
    // strArray[1] = optionsStr;
    optionsStr = env->NewStringUTF(options);
    env->SetObjectArrayElement(strArray, 1, optionsStr);

    // 至此,参数数组包含了两个元素,第一个是类名,第二个是选项字符串。

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className); // 将Java风格的类路径转换为jni风格的类路径。
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        // 调用Java类中的main方法。
        // startClass.main(strArray)
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            // 调用main方法。
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly\n");
}

  • 上面的代码中调用了Android::startVm函数启动了虚拟机:
/*
 * 启动 Dalvik 虚拟机。
 *
 * Various arguments, most determined by system properties, are passed in.
 * The "mOptions" vector is updated.
 *
 * 返回 0 代表成功。
 */
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
    ......

    // checkjni就是我们在C++层调用jni函数的时候,会对参数或者什么的进行检查,
    // 要是不合法的话,直接把虚拟机exit!第一个影响速度,第二个是影响程序,这个只作为开发时候使用。
    // 但是开启这个标志也有显著的好处,如果jni程序有问题,那么可以准确的爆出是哪里的问题!
    property_get("dalvik.vm.checkjni", propBuf, "");
    if (strcmp(propBuf, "true") == 0) {
        checkJni = true;
    } else if (strcmp(propBuf, "false") != 0) {
        /* property is neither true nor false; fall back on kernel parameter */
        property_get("ro.kernel.android.checkjni", propBuf, "");
        if (propBuf[0] == '1') {
            checkJni = true;
        }
    }

    // 虚拟机模式。
    property_get("dalvik.vm.execution-mode", propBuf, "");
    if (strcmp(propBuf, "int:portable") == 0) {
        executionMode = kEMIntPortable;
    } else if (strcmp(propBuf, "int:fast") == 0) {
        executionMode = kEMIntFast;
    } else if (strcmp(propBuf, "int:jit") == 0) {
        executionMode = kEMJitCompiler;
    }

    ......

    /*
     * 设置虚拟机最大heapsize。貌似最大才给16m设置的有点少。
     * The default starting and maximum size of the heap.  Larger
     * values should be specified in a product property override.
     */
    strcpy(heapstartsizeOptsBuf, "-Xms");
    property_get("dalvik.vm.heapstartsize", heapstartsizeOptsBuf+4, "4m");
    opt.optionString = heapstartsizeOptsBuf;
    mOptions.add(opt);
    strcpy(heapsizeOptsBuf, "-Xmx");
    property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");
    opt.optionString = heapsizeOptsBuf;
    mOptions.add(opt);

    // Increase the main thread's interpreter stack size for bug 6315322.
    opt.optionString = "-XX:mainThreadStackSize=24K";
    mOptions.add(opt);

    ......

    /*
     * We don't have /tmp on the device, but we often have an SD card.  Apps
     * shouldn't use this, but some test suites might want to exercise it.
     */
    opt.optionString = "-Djava.io.tmpdir=/sdcard";
    mOptions.add(opt);

    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray();
    initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;

    /*
     * 具体dalvik虚拟机有哪些参数,可以参考Dalvik的说明,
     * 反正调用了下面这个函数,虚拟机就按您指定的参数启动了。
     * Initialize the VM.
     *
     * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
     * If this call succeeds, the VM is ready, and we can start issuing
     * JNI calls.
     */
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        goto bail;
    }

    result = 0;

bail:
    free(stackTraceFile);
    return result;
}

上面的代码中获得了大量属性设置,和做了一些检查。上面的mOptions成员变量的类型是Vector<JavaVMOption>

JavaVMOption结构体:

/*
 * JNI 1.2+ initialization.  (As of 1.6, the pre-1.2 structures are no
 * longer supported.)
 */
typedef struct JavaVMOption {
    const char* optionString;
    void*       extraInfo;
} JavaVMOption;

  • startVm函数中调用JNI_CreateJavaVM函数创建虚拟机,这个函数在dalvik/vm/Jni.cpp中:
/*
 * 创建 VM 实例。
 * Create a new VM instance.
 *
 * 当前线程成为主 VM 线程。
 * The current thread becomes the main VM thread.  We return immediately,
 * which effectively means the caller is executing in a native method.
 */
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
    const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
    // 判断Jni版本是否合法。
    if (dvmIsBadJniVersion(args->version)) {
        ALOGE("Bad JNI version passed to CreateJavaVM: %d", args->version);
        return JNI_EVERSION;
    }

    // TODO: don't allow creation of multiple VMs -- one per customer for now

    // 全局变量"struct DvmGlobals gDvm;"在这个函数中被初始赋值。
    /* zero globals; not strictly necessary the first time a VM is started */
    memset(&gDvm, 0, sizeof(gDvm));

    /*
     * Set up structures for JNIEnv and VM.
     */
    JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
    pVM->funcTable = &gInvokeInterface;
    pVM->envList = NULL;
    // dvmInitMutex函数在"dalvik/vm/Thread.h"中定义,用于初始化互斥锁。
    dvmInitMutex(&pVM->envListLock);

    UniquePtr<const char*[]> argv(new const char*[args->nOptions]);
    memset(argv.get(), 0, sizeof(char*) * (args->nOptions));

    /*
     * Convert JNI args to argv.
     *
     * We have to pull out vfprintf/exit/abort, because they use the
     * "extraInfo" field to pass function pointer "hooks" in.  We also
     * look for the -Xcheck:jni stuff here.
     */
    int argc = 0;
    for (int i = 0; i < args->nOptions; i++) {
        ......
        在这个循环中对参数进行解析,并对gDvm进行了赋值。
    }

    if (gDvmJni.useCheckJni) {
        dvmUseCheckedJniVm(pVM);
    }

    if (gDvmJni.jniVm != NULL) {
        dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");
        free(pVM);
        return JNI_ERR;
    }
    // gDvmJni的JavaVM*类型在这里被赋值。
    gDvmJni.jniVm = (JavaVM*) pVM;

    // 为主线程创建JNIEnv。
    /*
     * Create a JNIEnv for the main thread.  We need to have something set up
     * here because some of the class initialization we do when starting
     * up the VM will call into native code.
     */
    JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);

    // 初始化 VM。
    /* Initialize VM. */
    gDvm.initializing = true;
    // argc: 参数个数。
    // argv: 参数数组。
    std::string status =
            dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
    gDvm.initializing = false;

    // 如果返回的字符串不为空,则创建VM失败。
    if (!status.empty()) {
        free(pEnv);
        free(pVM);
        ALOGW("CreateJavaVM failed: %s", status.c_str());
        return JNI_ERR;
    }

    /*
     * Success!  Return stuff to caller.
     */
    dvmChangeStatus(NULL, THREAD_NATIVE);
    *p_env = (JNIEnv*) pEnv;
    *p_vm = (JavaVM*) pVM;
    ALOGV("CreateJavaVM succeeded");
    return JNI_OK;
}

JavaVMExt结构体:

struct JavaVMExt {
    // funcTable通常被赋值为全局变量:gInvokeInterface。
    const struct JNIInvokeInterface* funcTable;     /* must be first */

    const struct JNIInvokeInterface* baseFuncTable;

    /* head of list of JNIEnvs associated with this VM */
    JNIEnvExt*      envList;    // JNIEnv列表头。
    pthread_mutex_t envListLock;// 互斥锁。被dvmInitMutex函数初始化。
};

全局变量gInvokeInterface

static const struct JNIInvokeInterface gInvokeInterface = {
    NULL,
    NULL,
    NULL,

    DestroyJavaVM,
    AttachCurrentThread,
    DetachCurrentThread,

    GetEnv,

    AttachCurrentThreadAsDaemon,
};

JavaVMInitArgs结构体:

typedef struct JavaVMInitArgs {
    // JNI版本。
    jint        version;    /* use JNI_VERSION_1_2 or later */

    jint        nOptions;    // options数组的元素个数。
    JavaVMOption* options;   // JavaVMOption数组。
    jboolean    ignoreUnrecognized;
} JavaVMInitArgs;

在JNI_CreateJavaVM函数中初始化了全局变量gDvm并赋值,还创建了JavaVMExt。


  • dvmCreateJNIEnv

    JNI_CreateJavaVM调用了dvmCreateJNIEnv函数。dvmCreateJNIEnv函数创建一个JNIEnvExt结构,函数返回时,将JNIEnvExt*强制转换为JNIEnv*:

/*
 * 创建一个新的JNIEnvExt结构,并将它添加到VM列表中。
 * 返回时,将JNIEnvExt*强制转换为JNIEnv*。
 * Create a new JNIEnv struct and add it to the VM's list.
 *
 * "self"为NULL,表示这是主线程,因为VM还未启动。这个值在随后将被填充。
 * "self" will be NULL for the main thread, since the VM hasn't started
 * yet; the value will be filled in later.
 */
JNIEnv* dvmCreateJNIEnv(Thread* self) {
    JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;

    //if (self != NULL)
    //    ALOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);

    assert(vm != NULL);

    JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
    newEnv->funcTable = &gNativeInterface;
    if (self != NULL) {
        dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
        assert(newEnv->envThreadId != 0);
    } else {
        /* make it obvious if we fail to initialize these later */
        newEnv->envThreadId = 0x77777775;
        newEnv->self = (Thread*) 0x77777779;
    }
    if (gDvmJni.useCheckJni) {
        dvmUseCheckedJniEnv(newEnv);
    }

    ScopedPthreadMutexLock lock(&vm->envListLock);

    // 在JavaVMExt的JNIEnvExt列表(envList)的起始位置插入新的JNIEnvExt结构(newEnv)。
    /* insert at head of list */
    newEnv->next = vm->envList;
    assert(newEnv->prev == NULL);
    if (vm->envList == NULL) {
        // 罕见,但可能
        // rare, but possible
        vm->envList = newEnv;
    } else {
        vm->envList->prev = newEnv;
    }
    vm->envList = newEnv;

    //if (self != NULL)
    //    ALOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
    return (JNIEnv*) newEnv;
}

全局变量gNativeInterfacedalvik/vm/Jni.cpp文件中。

JNIEnvExt结构:

struct JNIEnvExt {
    const struct JNINativeInterface* funcTable;     /* must be first */

    const struct JNINativeInterface* baseFuncTable;

    u4      envThreadId;
    Thread* self;

    /* if nonzero, we are in a "critical" JNI call */
    int     critical;

    struct JNIEnvExt* prev;
    struct JNIEnvExt* next;
};
  • funcTable中保存的是&gNativeInterface
  • envThreadId中保存绑定JNIEnvExt结构的线程的Id。
  • self指向的是绑定JNIEnvExt的线程结构。
  • prev中保存的是前一个JNIEnvExt结构的地址。如果当前节点是头节点,那么它的值是NULL。
  • next中保存的是后一个JNIEnvExt结构的地址。如果当前节点是尾节点,那么它的值是NULL。

由于JNI_CreateJavaVM函数调用dvmCreateJNIEnv传入的参数是NULL,所以JNIEnvExt.self为0x77777779,JNIEnvExt.envThreadId为0x77777775,可以肯定的是这两个初始值肯定是不正常的值。


时间: 2024-10-17 16:58:02

【分析】dalvik虚拟机启动过程(一)的相关文章

【分析】dalvik虚拟机启动过程(二)

源码版本:Android-4.4.4_r2 提示:大部分分析直接注释在代码内. 接着上一篇[分析]dalvik虚拟机启动过程(一) JNI_CreateJavaVM函数调用dvmCreateJNIEnv创建JNIEnv后,接着又调用了dvmStartup函数初始化VM: /* * VM 初始化. * VM initialization. Pass in any options provided on the command line. * Do not pass in the class nam

【分析】dalvik虚拟机启动过程(三)

源码版本:Android-4.4.4_r2 提示:大部分分析直接注释在代码内. 相关文章: [分析]dalvik虚拟机启动过程(一) [分析]dalvik虚拟机启动过程(二) 在AndroidRuntime::start中调用AndroidRuntime::startVm函数启动了虚拟机,然后又调用了AndroidRuntime::startReg函数注册Android函数: /* * 向 VM 注册Android本地函数. * Register android native functions

Android 源码分析-Dalvik 虚拟机创建过程

更多完整项目下载.未完待续.源码.图文知识后续上传github.可以点击关于我?联系我获取 一. 介绍Dalvik 1.java的运行需要JVM,同样android中使用了java语言,也需要一个VM.针对手机处理器和内存等硬件资源不足而推出来的一款VM,为android运行提供环境,叫DVM. 2.Dalvik虚拟机允许多个instance的存在.实际上android中的每一个app都是运行在自己VM实例之中(沙盒).每一个VM实例在linux中又是一个单独的进程,所以可以认为是同一个概念.运

openstack学习笔记一 虚拟机启动过程代码跟踪

本文主要通过对虚拟机创建过程的代码跟踪.观察虚拟机启动任务状态的变化,来透彻理解openstack各组件之间的作用过程. 当从horizon界面发送一个创建虚拟机请求,horizon api 将会依据前端给定的数据信息.调用novaclient 生成一个创建虚拟机的http post 请求来创建vm服务. >/usr/lib/python2.6/site-packages/horizon/api/nova.py(334)server_create() > /usr/lib/python2.6/

一步一步分析新建App启动过程

IOS温故而知新(一)  一步一步分析新建App启动过程 笔者接触IOS大概半年的时间,可以说能够开发App,但是又有很多不足的地方,因此打算写一些技术文章,能够达到温故而知新的效果,难免会有错误或者遗漏,也希望各位看官不吝指教.另外宣传一下刚建立的群“IOS菜鸟到高手之路”,312747764,欢迎希望交流分享的人加入. 1.IOS系统的简单说明IOS与Mac OS X以及安卓一样都属于类UINX系统,然而熟悉类UNIX系统的人可能会失望的是,个人开发的App会进入类似chroot的状态,被操

Android 源码分析(十) Dalvik 虚拟机创建过程

一. 介绍Dalvik 1.java的运行需要JVM(后面有大量篇幅介绍),同样android中使用了java语言,也需要一个VM.针对手机处理器和内存等硬件资源不足而推出来的一款VM,为android运行提供环境,叫DVM. 2.Dalvik虚拟机允许多个instance的存在.实际上android中的每一个app都是运行在自己VM实例之中(沙盒).每一个VM实例在linux中又是一个单独的进程,所以可以认为是同一个概念.运行在自己的DVM进程之中,不同的app不会相互干扰,且不会因为一个DV

linux内核学习之三 跟踪分析内核的启动过程

一   前期准备工作       1 搭建环境 1.1下载内核源代码并编译内核 创建目录,并进入该目录: 下载源码: 解压缩,并进入该目录:xz -d linux-3.18.6.tar.xz tar -xvf linux-3.18.6 cd  linux-3.18.6 选定x86架构的相关文件编译: 编译: 1.2 制作根文件系统 在工作目录下新建一个文件夹: mkdir rootfs 下载老师提供的资料:git clone https://github.com/mengning/menu.gi

wxWidgets源码分析(1) - App启动过程

目录 APP启动过程 wxApp入口定义 wxApp实例化准备 wxApp的实例化 wxApp运行 总结 APP启动过程 本文主要介绍wxWidgets应用程序的启动过程,从app.cpp入手. wxApp入口定义 wxApp通过IMPLEMENT_APP宏注册App类,这个宏同时定义了入口,实现在wx/app.h文件中. // wx/app.h 文件中定义 #define IMPLEMENT_APP(app) wxIMPLEMENT_APP(app); // 可以忽略 wxIMPLEMENT_

NodeManager代码分析之NodeManager启动过程

1.NodeManager概述 NodeManager(NM)是YARN中每个节点上的代理,它管理Hadoop集群中单个计算节点,包括与ResourceManger保持通信,监督Container的生命周期管理,监控每个Container的资源使用(内存.CPU等)情况,追踪节点健康状况,管理日志和不同应用程序用到的附属服务. NodeManager整体架构: 2.NodeManager分析 接下来将按照启动NodeManager时代码执行的顺序为主线进行代码分析. 2.1 main函数 打印N