源码版本: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; }
全局变量gNativeInterface
在dalvik/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