Dalvik学习笔记--启动过程

来源:互联网 发布:如何使用网络 编辑:程序博客网 时间:2024/06/05 20:22

学习老罗的博客,顺便记点笔记,强化记忆

代码用的4.3,与老罗不一样的地方会标注出来

从AndroidRuntime.start开始

void AndroidRuntime::start(const char* className, const char* options){    ......    /* start the virtual machine */    JNIEnv* env;    if (startVm(&mJavaVM, &env) != 0) {        return;    }    onVmCreated(env);<span style="white-space:pre"></span>//此函数为空函数,原文注释:If AndroidRuntime had anything to do here, we'd have done it in 'start'.    /*     * 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;    stringClass = env->FindClass("java/lang/String");<span style="white-space:pre">    assert(stringClass != NULL);    strArray = env->NewObjectArray(2, stringClass, NULL);    assert(strArray != NULL);    classNameStr = env->NewStringUTF(className);    assert(classNameStr != NULL);    env->SetObjectArrayElement(strArray, 0, classNameStr);<span style="white-space:pre"></span>//构造2个元素的String数组    optionsStr = env->NewStringUTF(options);<span style="white-space:pre"></span>//第一个元素为类名    env->SetObjectArrayElement(strArray, 1, optionsStr);<span style="white-space:pre"></span>//第二个为设置    /*     * Start VM.  This thread becomes the main thread of the VM, and will     * not return until the VM exits.     */    char* slashClassName = toSlashClassName(className);<span style="white-space:pre"></span>//将字符串中的 . 替换为 /    jclass startClass = env->FindClass(slashClassName);    if (startClass == NULL) {        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);        /* keep going */    } else {        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",            "([Ljava/lang/String;)V");<span style="white-space:pre"></span>//获取main方法的id        if (startMeth == NULL) {            ALOGE("JavaVM unable to find main() in '%s'\n", className);            /* keep going */        } else {            env->CallStaticVoidMethod(startClass, startMeth, strArray);<span style="white-space:pre"></span>//调用main方法    ......}

GetStaticMethodID函数用于获取方法id,一个参数为目标类,第二个为方法名,第三个是参数描述(是不是很像smali代码)

CallStaticVoidMethod函数第一个参数为目标类,第二个为方法id,第三个是可变参数

这里有个小小的变化就是toSlashClassName将原来的几行代码封装了一下。

实例创建startVm函数

该函数很长,但是大致可以分为三部分

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv){    int result = -1;    JavaVMInitArgs initArgs;    JavaVMOption opt;    char propBuf[PROPERTY_VALUE_MAX];    char stackTraceFileBuf[PROPERTY_VALUE_MAX];    char dexoptFlagsBuf[PROPERTY_VALUE_MAX];    char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];    char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];    char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];    char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];    char heapgrowthlimitOptsBuf[sizeof("-XX:HeapGrowthLimit=")-1 + PROPERTY_VALUE_MAX];    char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX];    char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX];    char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];    char extraOptsBuf[PROPERTY_VALUE_MAX];    char* stackTraceFile = NULL;    bool checkJni = false;    bool checkDexSum = false;    bool logStdio = false;    enum {      kEMDefault,      kEMIntPortable,      kEMIntFast,      kEMJitCompiler,    } executionMode = kEMDefault;
先声明字符串用于存放配置信息,声明标志变量

    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;        }    }    ......    /* Force interpreter-only mode for selected methods */    char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];    property_get("dalvik.vm.jit.method", propBuf, "");    if (strlen(propBuf) > 0) {        strcpy(jitMethodBuf, "-Xjitmethod:");        strcat(jitMethodBuf, propBuf);        opt.optionString = jitMethodBuf;        mOptions.add(opt);    }
第二部分大多都是上述这种形式,先用property_get函数,在之前声明的字符串中存放配置信息,比较后设置对应的标志变量

比如在propBuf中存放dalvik.vm.checkjni的信息,如果propBuf字符串为“true”,就将checkJni设置为true

    if (executionMode == kEMIntPortable) {        opt.optionString = "-Xint:portable";        mOptions.add(opt);    } else if (executionMode == kEMIntFast) {        opt.optionString = "-Xint:fast";        mOptions.add(opt);    } else if (executionMode == kEMJitCompiler) {        opt.optionString = "-Xint:jit";        mOptions.add(opt);    }    if (checkDexSum) {        /* perform additional DEX checksum tests */        opt.optionString = "-Xcheckdexsum";        mOptions.add(opt);    }    ......
再根据这些标志变量进行设置。

    /*     * 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;    }

当然最后调用JNI_CreateJavaVM函数。

/* * Create a new VM instance. * * 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;    if (args->version < JNI_VERSION_1_2) {        return JNI_EVERSION;    }    // TODO: don't allow creation of multiple VMs -- one per customer for now    /* 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(&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++) {<span style="white-space:pre"></span>//收集虚拟机相关信息        const char* optStr = args->options[i].optionString;        if (optStr == NULL) {            dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i);            return JNI_ERR;        } else if (strcmp(optStr, "vfprintf") == 0) {            gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;        } else if (strcmp(optStr, "exit") == 0) {            gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;        } else if (strcmp(optStr, "abort") == 0) {            gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;        } else if (strcmp(optStr, "sensitiveThread") == 0) {            gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;        } else if (strcmp(optStr, "-Xcheck:jni") == 0) {            gDvmJni.useCheckJni = true;        } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {            char* jniOpts = strdup(optStr + 10);            size_t jniOptCount = 1;            for (char* p = jniOpts; *p != 0; ++p) {                if (*p == ',') {                    ++jniOptCount;                    *p = 0;                }            }            char* jniOpt = jniOpts;            for (size_t i = 0; i < jniOptCount; ++i) {                if (strcmp(jniOpt, "warnonly") == 0) {                    gDvmJni.warnOnly = true;                } else if (strcmp(jniOpt, "forcecopy") == 0) {                    gDvmJni.forceCopy = true;                } else if (strcmp(jniOpt, "logThirdPartyJni") == 0) {                    gDvmJni.logThirdPartyJni = true;                } else {                    dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n",                            jniOpt);                    return JNI_ERR;                }                jniOpt += strlen(jniOpt) + 1;            }            free(jniOpts);        } else {            /* regular option */            argv[argc++] = optStr;        }    }    if (gDvmJni.useCheckJni) {        dvmUseCheckedJniVm(pVM);    }    if (gDvmJni.jniVm != NULL) {        dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");        return JNI_ERR;    }    gDvmJni.jniVm = (JavaVM*) pVM;    /*     * 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);    /* Initialize VM. */    gDvm.initializing = true;    std::string status =            dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);<span style="white-space:pre"></span>//初始化虚拟机,关键函数,其他代码都是为他的参数做准备    gDvm.initializing = false;    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;}
代码注释很全,简单说一下。先给一个虚拟机实例分配空间,初始化需要切换当前线程状态,需要保存设置和创建一个运行环境,gDvm就是用来收集虚拟机信息的全局变量(给实例分配的空间也保存在当中),用于在不同线程状态间传递虚拟机实例,argv保存从vm_args传递过来的参数,通过这个几个变量就可以切换线程状态创建虚拟机实例。创建完成后再通过dvmChangeStatus切换回去(Return stuff to caller)。然后将实例(pVM)和环境(pEnv)传递给调用者。

上述提到的DvmGlobals结构体定义文件dalvik/vm/Globals.h中,JNIInvokeInterface结构体定义在文件dalvik/libnativehelper/include/nativehelper/jni.h中,JavaVMExt和JNIEnvExt结构体定义在文件dalvik/vm/JniInternal.h中。

接下来是dvmCreateJNIEnv函数

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));<span style="white-space:pre"></span>//创建一个JNIEnvExt对象    newEnv->funcTable = &gNativeInterface;<span style="white-space:pre"></span>//设置本地借口表    if (self != NULL) {<span style="white-space:pre"></span>//self表示所要关联的线程        dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);<span style="white-space:pre"></span>//设置关联的函数        assert(newEnv->envThreadId != 0);    } else {        /* make it obvious if we fail to initialize these later */        newEnv->envThreadId = 0x77777775;<span style="white-space:pre"></span>//表示还未与线程关联        newEnv->self = (Thread*) 0x77777779;    }    if (gDvmJni.useCheckJni) {        dvmUseCheckedJniEnv(newEnv);    }    ScopedPthreadMutexLock lock(&vm->envListLock);    /* insert at head of list */    newEnv->next = vm->envList;<span style="white-space:pre"></span>//newEnv的宿主虚拟机是vm,也就是之前创建的实例    assert(newEnv->prev == NULL);    if (vm->envList == NULL) {<span style="white-space:pre"></span>//将newEnv插入到vm->envList链表中        // 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;}
 在一个Dalvik虚拟机里面,可以运行多个线程。所有关联有JNI环境的线程都有一个对应的JNIEnvExt对象,这些JNIEnvExt对象相互连接在一起保存在用来描述其宿主Dalvik虚拟机的一个JavaVMExt对象的成员变量envList中。因此,前面创建的JNIEnvExt对象需要连接到其宿主Dalvik虚拟机的JavaVMExt链表中去。
再看dvmStartup函数

std::string dvmStartup(int argc, const char* const argv[],        bool ignoreUnrecognized, JNIEnv* pEnv){    ScopedShutdown scopedShutdown;    assert(gDvm.initializing);    ALOGV("VM init args (%d):", argc);    for (int i = 0; i < argc; i++) {        ALOGV("  %d: '%s'", i, argv[i]);    }    setCommandLineDefaults();<span style="white-space:pre"></span>//设置默认项    /*     * Process the option flags (if any).     */    int cc = processOptions(argc, argv, ignoreUnrecognized);<span style="white-space:pre"></span>//处理启动选项    if (cc != 0) {        if (cc < 0) {            dvmFprintf(stderr, "\n");            usage("dalvikvm");        }        return "syntax error";    }
与老罗代码相比没有了dvmPropertiesStartup来分配空间
    /*     * Initialize components.     */    dvmQuasiAtomicsStartup();    if (!dvmAllocTrackerStartup()) {<span style="white-space:pre"></span>//对象分配记录子模块        return "dvmAllocTrackerStartup failed";    }    if (!dvmGcStartup()) {<span style="white-space:pre"></span>//GC子模块        return "dvmGcStartup failed";    }    if (!dvmThreadStartup()) {<span style="white-space:pre"></span>//线程列表        return "dvmThreadStartup failed";    }    if (!dvmInlineNativeStartup()) {<span style="white-space:pre"></span>//内建Native函数表        return "dvmInlineNativeStartup";    }    if (!dvmRegisterMapStartup()) {<span style="white-space:pre"></span>//寄存器映射集        return "dvmRegisterMapStartup failed";    }    if (!dvmInstanceofStartup()) {<span style="white-space:pre"></span>//实例操作符子模块        return "dvmInstanceofStartup failed";    }    if (!dvmClassStartup()) {<span style="white-space:pre"></span>//启动类加载器        return "dvmClassStartup failed";    }    /*     * At this point, the system is guaranteed to be sufficiently     * initialized that we can look up classes and class members. This     * call populates the gDvm instance with all the class and member     * references that the VM wants to use directly.     */    if (!dvmFindRequiredClassesAndMembers()) {<span style="white-space:pre"></span>//重要类和函数        return "dvmFindRequiredClassesAndMembers failed";    }    if (!dvmStringInternStartup()) {<span style="white-space:pre"></span>//字符串池        return "dvmStringInternStartup failed";    }    if (!dvmNativeStartup()) {<span style="white-space:pre"></span>//so库加载表        return "dvmNativeStartup failed";    }    if (!dvmInternalNativeStartup()) {<span style="white-space:pre"></span>//内部Native函数表        return "dvmInternalNativeStartup failed";    }    if (!dvmJniStartup()) {<span style="white-space:pre"></span>//全局引用表        return "dvmJniStartup failed";    }    if (!dvmProfilingStartup()) {<span style="white-space:pre"></span>//性能分析子模块        return "dvmProfilingStartup failed";    }    /*     * Create a table of methods for which we will substitute an "inline"     * version for performance.     */    if (!dvmCreateInlineSubsTable()) {<span style="white-space:pre"></span>//内联函数表        return "dvmCreateInlineSubsTable failed";    }    /*     * Miscellaneous class library validation.     */    if (!dvmValidateBoxClasses()) {<span style="white-space:pre"></span>//验证虚拟机中相应的装箱类        return "dvmValidateBoxClasses failed";    }    /*     * Do the last bits of Thread struct initialization we need to allow     * JNI calls to work.     */    if (!dvmPrepMainForJni(pEnv)) {<span style="white-space:pre"></span>//准备主线程JNI环境        return "dvmPrepMainForJni failed";    }    /*     * Explicitly initialize java.lang.Class.  This doesn't happen     * automatically because it's allocated specially (it's an instance     * of itself).  Must happen before registration of system natives,     * which make some calls that throw assertions if the classes they     * operate on aren't initialized.     */    if (!dvmInitClass(gDvm.classJavaLangClass)) {//确保目标类初始化        return "couldn't initialized java.lang.Class";    }    /*     * Register the system native methods, which are registered through JNI.     */    if (!registerSystemNatives(pEnv)) {<span style="white-space:pre"></span>//为JAVA核心类注册JNI方法        return "couldn't register system natives";    }    /*     * Do some "late" initialization for the memory allocator.  This may     * allocate storage and initialize classes.     */    if (!dvmCreateStockExceptions()) {<span style="white-space:pre"></span>//预创建与内存相关的异样对象        return "dvmCreateStockExceptions failed";    }    /*     * At this point, the VM is in a pretty good state.  Finish prep on     * the main thread (specifically, create a java.lang.Thread object to go     * along with our Thread struct).  Note we will probably be executing     * some interpreted class initializer code in here.     */    if (!dvmPrepMainThread()) {<span style="white-space:pre"></span>//为主线程创建ThreadGroup对象        return "dvmPrepMainThread failed";    }    /*     * Make sure we haven't accumulated any tracked references.  The main     * thread should be starting with a clean slate.     */    if (dvmReferenceTableEntries(&dvmThreadSelf()->internalLocalRefTable) != 0)<span style="white-space:pre"></span>//确保主线程当前不应用JAVA对象,保证一个干净的入口    {        ALOGW("Warning: tracked references remain post-initialization");        dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");    }    /* general debugging setup */    if (!dvmDebuggerStartup()) {<span style="white-space:pre"></span>//初始化调试环境        return "dvmDebuggerStartup failed";    }    if (!dvmGcStartupClasses()) {<span style="white-space:pre"></span>// GC class        return "dvmGcStartupClasses failed";    }
初始化各项子模块
    /*     * Init for either zygote mode or non-zygote mode.  The key difference     * is that we don't start any additional threads in Zygote mode.     */    if (gDvm.zygote) {        if (!initZygote()) {            return "initZygote failed";        }    } else {        if (!dvmInitAfterZygote()) {            return "dvmInitAfterZygote failed";        }    }
判断是否在zygote中启动虚拟机,注意是initZygote不是dvmInitZygote

/* * Do zygote-mode-only initialization. */static bool initZygote(){    /* zygote goes into its own process group */    setpgid(0,0);    // See storage config details at http://source.android.com/tech/storage/    // Create private mount namespace shared by all children    if (unshare(CLONE_NEWNS) == -1) {        SLOGE("Failed to unshare(): %s", strerror(errno));        return -1;    }    // Mark rootfs as being a slave so that changes from default    // namespace only flow into our children.    if (mount("rootfs", "/", NULL, (MS_SLAVE | MS_REC), NULL) == -1) {        SLOGE("Failed to mount() rootfs as MS_SLAVE: %s", strerror(errno));        return -1;    }    // Create a staging tmpfs that is shared by our children; they will    // bind mount storage into their respective private namespaces, which    // are isolated from each other.    const char* target_base = getenv("EMULATED_STORAGE_TARGET");    if (target_base != NULL) {        if (mount("tmpfs", target_base, "tmpfs", MS_NOSUID | MS_NODEV,                "uid=0,gid=1028,mode=0050") == -1) {            SLOGE("Failed to mount tmpfs to %s: %s", target_base, strerror(errno));            return -1;        }    }    // Mark /system as NOSUID | NODEV    const char* android_root = getenv("ANDROID_ROOT");    if (android_root == NULL) {        SLOGE("environment variable ANDROID_ROOT does not exist?!?!");        return -1;    }    std::string mountDev(getMountsDevDir(android_root));    if (mountDev.empty()) {        SLOGE("Unable to find mount point for %s", android_root);        return -1;    }    if (mount(mountDev.c_str(), android_root, "none",            MS_REMOUNT | MS_NOSUID | MS_NODEV | MS_RDONLY | MS_BIND, NULL) == -1) {        SLOGE("Remount of %s failed: %s", android_root, strerror(errno));        return -1;    }#ifdef HAVE_ANDROID_OS    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {        if (errno == EINVAL) {            SLOGW("PR_SET_NO_NEW_PRIVS failed. "                  "Is your kernel compiled correctly?: %s", strerror(errno));            // Don't return -1 here, since it's expected that not all            // kernels will support this option.        } else {            SLOGW("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));            return -1;        }    }#endif    return true;}
注释比较全,主要是父子进程共享资源的问题。

到这里实例的创建和初始化工作就算完成了,dvmStartup函数也算结束了,要注意的是dvmStartup函数反回的是一个字符串。

JNI_CreateJavaVM函数中是这样调用的

    std::string status =            dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
    if (!status.empty()) {        free(pEnv);        free(pVM);        ALOGW("CreateJavaVM failed: %s", status.c_str());        return JNI_ERR;    }
返回到AndroidRuntime::start,之后执行的函数是startReg,注册Android核心类的JNI方法

int AndroidRuntime::startReg(JNIEnv* env){    /*     * This hook causes all future threads created in this process to be     * attached to the JavaVM.  (This needs to go away in favor of JNI     * Attach calls.)     */    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);    ALOGV("--- registering native functions ---\n");    /*     * Every "register" function calls one or more things that return     * a local reference (e.g. FindClass).  Because we haven't really     * started the VM yet, they're all getting stored in the base frame     * and never released.  Use Push/Pop to manage the storage.     */    env->PushLocalFrame(200);    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {        env->PopLocalFrame(NULL);        return -1;    }    env->PopLocalFrame(NULL);    //createJavaThread("fubar", quickTest, (void*) "hello");    return 0;}
总结:创建实例,处理配置信息 ——> 收集配置信息,创建环境 ——> 切换线程状态,初始化虚拟机实例 ——> 注册核心方法 ——> 启动main方法

最后附上老罗的图









0 0
原创粉丝点击