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

时间:2022-12-23 19:09:14

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