源码版本: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
,可以肯定的是这两个初始值肯定是不正常的值。