安卓高手之路之ClassLoader(二) - 修补C++ - ITeye技术网站
因为ClassLoader一定与虚拟机的启动有关系,那么必须从Zygote的启动开始看代码。下面就分析一下这些代码,行数不多:
- int main(int argc, const char* const argv[])
- {
- // These are global variables in ProcessState.cpp
- //ProcessState.cpp中可能要用到一些main函数。
- mArgC = argc;
- mArgV = argv;
- mArgLen = 0;
- for (int i=0; i<argc; i++) {
- mArgLen += strlen(argv[i]) + 1;
- }
- mArgLen--;
- AppRuntime runtime;
- const char* argv0 = argv[0];
- // Process command line arguments
- // ignore argv[0]
- argc--;
- argv++;
- // Everything up to '--' or first non '-' arg goes to the vm
- int i = runtime.addVmArguments(argc, argv);
- // Parse runtime arguments. Stop at first unrecognized option.
- bool zygote = false;
- bool startSystemServer = false;
- bool application = false;
- const char* parentDir = NULL;
- const char* niceName = NULL;
- const char* className = NULL;
- while (i < argc) {
- const char* arg = argv[i++];
- if (!parentDir) {
- parentDir = arg;
- } else if (strcmp(arg, "--zygote") == 0) {
- zygote = true;
- niceName = "zygote";
- } else if (strcmp(arg, "--start-system-server") == 0) {
- startSystemServer = true;
- } else if (strcmp(arg, "--application") == 0) {
- application = true;
- } else if (strncmp(arg, "--nice-name=", 12) == 0) {
- niceName = arg + 12;
- } else {
- className = arg;
- break;
- }
- }
- if (niceName && *niceName) {
- setArgv0(argv0, niceName);
- set_process_name(niceName);
- }
- runtime.mParentDir = parentDir;
- if (zygote) {
- runtime.start("com.android.internal.os.ZygoteInit",
- startSystemServer ? "start-system-server" : "");
- } else if (className) {
- // Remainder of args get passed to startup class main()
- runtime.mClassName = className;
- runtime.mArgC = argc - i;
- runtime.mArgV = argv + i;
- runtime.start("com.android.internal.os.RuntimeInit",
- application ? "application" : "tool");
- } else {
- fprintf(stderr, "Error: no class name or --zygote supplied.\n");
- app_usage();
- LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
- return 10;
- }
- }
int main(int argc, const char* const argv[])
{
// These are global variables in ProcessState.cpp
//ProcessState.cpp中可能要用到一些main函数。
mArgC = argc;
mArgV = argv;
mArgLen = 0;
for (int i=0; i<argc; i++) {
mArgLen += strlen(argv[i]) + 1;
}
mArgLen--;
AppRuntime runtime;
const char* argv0 = argv[0];
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// Everything up to '--' or first non '-' arg goes to the vm
int i = runtime.addVmArguments(argc, argv);
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
const char* parentDir = NULL;
const char* niceName = NULL;
const char* className = NULL;
while (i < argc) {
const char* arg = argv[i++];
if (!parentDir) {
parentDir = arg;
} else if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = "zygote";
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = arg + 12;
} else {
className = arg;
break;
}
}
if (niceName && *niceName) {
setArgv0(argv0, niceName);
set_process_name(niceName);
}
runtime.mParentDir = parentDir;
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server" : "");
} else if (className) {
// Remainder of args get passed to startup class main()
runtime.mClassName = className;
runtime.mArgC = argc - i;
runtime.mArgV = argv + i;
runtime.start("com.android.internal.os.RuntimeInit",
application ? "application" : "tool");
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
return 10;
}
}分析完之后发现如下参数规律:
1. argv[0]:用这个修改了进程名称。
2. 虚拟机参数:前面的选项参数都是以“-”打头。被放入了runtime。这些参数被称为是虚拟机参数。
3.“--”打头的参数是zygote参数。有如下几种,排列顺序如下:
-runtimearg[0]
-runtimearg[1]
。。。。
parentDir //这个也是runtime使用的,也就是VM使用的。
className//这个也是runtime使用的,也就是VM使用的。
--zygote
--start-system-server
--application
--nice-name=
然后,如果是zygote,那么进入下面这句话
- runtime.start("com.android.internal.os.ZygoteInit",
- startSystemServer ? "start-system-server" : "");
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer ? "start-system-server" : "");
如果有类名,那么进入下面这句话:
- runtime.mClassName = className;
- runtime.mArgC = argc - i; //className,包括className以后的参数个数。
- runtime.mArgV = argv + i; //截止到className的参数个数
- runtime.start("com.android.internal.os.RuntimeInit",
- application ? "application" : "tool");
runtime.mClassName = className;
runtime.mArgC = argc - i; //className,包括className以后的参数个数。
runtime.mArgV = argv + i; //截止到className的参数个数
runtime.start("com.android.internal.os.RuntimeInit",
application ? "application" : "tool");
第一部分:那么开机第一次启动的就一定是,
- runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : "");
runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : "");其中startSystemServer 由init.rc指定,在目录android40\system\core\rootdir中的init.rc.
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
第二部分:从ActivityManagerService可以看出,--application并没有指定,这句话也就相当于:
- runtime.start("com.android.internal.os.RuntimeInit", "tool");
runtime.start("com.android.internal.os.RuntimeInit", "tool");现在代码分成了两部分。
那么先分析第一部分。
那么zygote启动到底配置了那些参数呢,我们就看一看:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
根据上面说的参数序列图,可以看出。
runtime.mParentDir 为/system/bin
runtime的一个arg为-Xzygote
那么这个这个start函数就变成:
- runtime.start("com.android.internal.os.ZygoteInit", "start-system-server");
runtime.start("com.android.internal.os.ZygoteInit", "start-system-server");
代码进入到base/core/jni目录的AndroidRuntime.cpp里面。这个函数还不算长,就直接贴出来看一下,注意注释,由此可以看出这个就是启动虚拟机的代码所在啊。那么既然Zygote进程也是这么启动的,那么我们就有理由断定Zygote也是个Dalvik虚拟机!事情是不是这样呢?那么就带着这个疑问去分析一下:
- /*
- * Start the Android runtime. This involves starting the virtual machine
- * and calling the "static void main(String[] args)" method in the class
- * named by "className".
- *
- * Passes the main function two arguments, the class name and the specified
- * options string.
- */
- void AndroidRuntime::start(const char* className, const char* options)
- {
- LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
- className != NULL ? className : "(unknown)");
- blockSigpipe();
- /*
- * '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)));
- }
- 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");
- //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
- /* start the virtual machine */
- JNIEnv* env;
- if (startVm(&mJavaVM, &env) != 0) {
- return;
- }
- onVmCreated(env);
- /*
- * Register android functions.
- */
- if (startReg(env) < 0) {
- LOGE("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");
- assert(stringClass != NULL);
- strArray = env->NewObjectArray(2, stringClass, NULL);
- assert(strArray != NULL);
- classNameStr = env->NewStringUTF(className);
- assert(classNameStr != NULL);
- env->SetObjectArrayElement(strArray, 0, classNameStr);
- 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);
- jclass startClass = env->FindClass(slashClassName);
- if (startClass == NULL) {
- LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
- /* keep going */
- } else {
- jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
- "([Ljava/lang/String;)V");
- if (startMeth == NULL) {
- LOGE("JavaVM unable to find main() in '%s'\n", className);
- /* keep going */
- } else {
- env->CallStaticVoidMethod(startClass, startMeth, strArray);
- #if 0
- if (env->ExceptionCheck())
- threadExitUncaughtException(env);
- #endif
- }
- }
- free(slashClassName);
- LOGD("Shutting down VM\n");
- if (mJavaVM->DetachCurrentThread() != JNI_OK)
- LOGW("Warning: unable to detach main thread\n");
- if (mJavaVM->DestroyJavaVM() != 0)
- LOGW("Warning: VM did not shut down cleanly\n");
- }
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*
* Passes the main function two arguments, the class name and the specified
* options string.
*/
void AndroidRuntime::start(const char* className, const char* options)
{
LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
className != NULL ? className : "(unknown)");
blockSigpipe();
/*
* '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)));
}
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");
//LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
/* start the virtual machine */
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
LOGE("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");
assert(stringClass != NULL);
strArray = env->NewObjectArray(2, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
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);
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
LOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
LOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
LOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
LOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
LOGW("Warning: VM did not shut down cleanly\n");
}
linux的POSIX (Portable Operating System Interface of Unix)我不懂。但是从直观上看,可能是一种禁止打断进程的方法:
- LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
- className != NULL ? className : "(unknown)");
- blockSigpipe();
LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
className != NULL ? className : "(unknown)");
blockSigpipe();下面这句话毫无意义,就是打印log
- 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)));
- }
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)));
}下面这句话定义androidroot的目录
- 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* 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);
}对照init.rc可以知道,就是/system
- # setup the global environment
- export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
- export LD_LIBRARY_PATH /vendor/lib:/system/lib
- export ANDROID_BOOTLOGO 1
- export ANDROID_ROOT /system
- export ANDROID_ASSETS /system/app
- export ANDROID_DATA /data
- export ASEC_MOUNTPOINT /mnt/asec
- export LOOP_MOUNTPOINT /mnt/obb
- export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
# setup the global environment
export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
export LD_LIBRARY_PATH /vendor/lib:/system/lib
export ANDROID_BOOTLOGO 1
export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data
export ASEC_MOUNTPOINT /mnt/asec
export LOOP_MOUNTPOINT /mnt/obb
export BOOTCLASSPATH /system/framework/core.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/filterfw.jar
主要是下面这两句话
- /* start the virtual machine */
- JNIEnv* env;
- if (startVm(&mJavaVM, &env) != 0) {
- return;
- }
- onVmCreated(env);
- /*
- * Register android functions.
- */
- if (startReg(env) < 0) {
- LOGE("Unable to register all android natives\n");
- return;
- }
/* start the virtual machine */
JNIEnv* env;
if (startVm(&mJavaVM, &env) != 0) {
return;
}
onVmCreated(env);
/*
* Register android functions.
*/
if (startReg(env) < 0) {
LOGE("Unable to register all android natives\n");
return;
}
一个启动虚拟机,一个启动注册安卓本地方法。虚拟机的启动流程,最终调用的是
JNI_CreateJavaVM 在framework/base/core/jni/AndroidRuntime.cpp下。JNI_CreateJavaVM 调用的是:
然后调用dalvik/vm/Jni.cpp的JNI_CreateJavaVM 方法:
- 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*) malloc(sizeof(JavaVMExt));
- memset(pVM, 0, 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++) {
- 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);
- gDvm.initializing = false;
- if (!status.empty()) {
- free(pEnv);
- free(pVM);
- LOGW("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;
- LOGV("CreateJavaVM succeeded");
- return JNI_OK;
- }
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*) malloc(sizeof(JavaVMExt));
memset(pVM, 0, 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++) {
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);
gDvm.initializing = false;
if (!status.empty()) {
free(pEnv);
free(pVM);
LOGW("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;
LOGV("CreateJavaVM succeeded");
return JNI_OK;
}
然后调用Jni.cpp中的
- /*
- * Create a new JNIEnv struct and add it to the VM's list.
- *
- * "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)
- // LOGI("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);
- /* 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)
- // LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
- return (JNIEnv*) newEnv;
- }
/*
* Create a new JNIEnv struct and add it to the VM's list.
*
* "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)
// LOGI("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);
/* 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)
// LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
return (JNIEnv*) newEnv;
}
好吧,这些全是些乱七八糟的东西。真正启动的是这句话,Jni.cpp中:
- std::string status =
- dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
std::string status =
dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
在Dalvik/vm/Init.cpp中
- *
- * VM initialization. Pass in any options provided on the command line.
- * Do not pass in the class name or the options for the class.
- *
- * Returns 0 on success.
- */
- std::string dvmStartup(int argc, const char* const argv[],
- bool ignoreUnrecognized, JNIEnv* pEnv)
- {
- ScopedShutdown scopedShutdown;
- assert(gDvm.initializing);
- LOGV("VM init args (%d):", argc);
- for (int i = 0; i < argc; i++) {
- LOGV(" %d: '%s'", i, argv[i]);
- }
- setCommandLineDefaults();
- /*
- * Process the option flags (if any).
- */
- int cc = processOptions(argc, argv, ignoreUnrecognized);
- if (cc != 0) {
- if (cc < 0) {
- dvmFprintf(stderr, "\n");
- usage("dalvikvm");
- }
- return "syntax error";
- }
- #if WITH_EXTRA_GC_CHECKS > 1
- /* only "portable" interp has the extra goodies */
- if (gDvm.executionMode != kExecutionModeInterpPortable) {
- LOGI("Switching to 'portable' interpreter for GC checks");
- gDvm.executionMode = kExecutionModeInterpPortable;
- }
- #endif
- /* Configure group scheduling capabilities */
- if (!access("/dev/cpuctl/tasks", F_OK)) {
- LOGV("Using kernel group scheduling");
- gDvm.kernelGroupScheduling = 1;
- } else {
- LOGV("Using kernel scheduler policies");
- }
- /* configure signal handling */
- if (!gDvm.reduceSignals)
- blockSignals();
- /* verify system page size */
- if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
- return StringPrintf("expected page size %d, got %d",
- SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
- }
- /* mterp setup */
- LOGV("Using executionMode %d", gDvm.executionMode);
- dvmCheckAsmConstants();
- /*
- * Initialize components.
- */
- dvmQuasiAtomicsStartup();
- if (!dvmAllocTrackerStartup()) {
- return "dvmAllocTrackerStartup failed";
- }
- if (!dvmGcStartup()) {
- return "dvmGcStartup failed";
- }
- if (!dvmThreadStartup()) {
- return "dvmThreadStartup failed";
- }
- if (!dvmInlineNativeStartup()) {
- return "dvmInlineNativeStartup";
- }
- if (!dvmRegisterMapStartup()) {
- return "dvmRegisterMapStartup failed";
- }
- if (!dvmInstanceofStartup()) {
- return "dvmInstanceofStartup failed";
- }
- if (!dvmClassStartup()) {
- 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()) {
- return "dvmFindRequiredClassesAndMembers failed";
- }
- if (!dvmStringInternStartup()) {
- return "dvmStringInternStartup failed";
- }
- if (!dvmNativeStartup()) {
- return "dvmNativeStartup failed";
- }
- if (!dvmInternalNativeStartup()) {
- return "dvmInternalNativeStartup failed";
- }
- if (!dvmJniStartup()) {
- return "dvmJniStartup failed";
- }
- if (!dvmProfilingStartup()) {
- return "dvmProfilingStartup failed";
- }
- /*
- * Create a table of methods for which we will substitute an "inline"
- * version for performance.
- */
- if (!dvmCreateInlineSubsTable()) {
- return "dvmCreateInlineSubsTable failed";
- }
- /*
- * Miscellaneous class library validation.
- */
- if (!dvmValidateBoxClasses()) {
- return "dvmValidateBoxClasses failed";
- }
- /*
- * Do the last bits of Thread struct initialization we need to allow
- * JNI calls to work.
- */
- if (!dvmPrepMainForJni(pEnv)) {
- 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)) {
- return "couldn't register system natives";
- }
- /*
- * Do some "late" initialization for the memory allocator. This may
- * allocate storage and initialize classes.
- */
- if (!dvmCreateStockExceptions()) {
- 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()) {
- 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)
- {
- LOGW("Warning: tracked references remain post-initialization");
- dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
- }
- /* general debugging setup */
- if (!dvmDebuggerStartup()) {
- return "dvmDebuggerStartup failed";
- }
- if (!dvmGcStartupClasses()) {
- 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";
- }
- }
- #ifndef NDEBUG
- if (!dvmTestHash())
- LOGE("dvmTestHash FAILED");
- if (false /*noisy!*/ && !dvmTestIndirectRefTable())
- LOGE("dvmTestIndirectRefTable FAILED");
- #endif
- if (dvmCheckException(dvmThreadSelf())) {
- dvmLogExceptionStackTrace();
- return "Exception pending at end of VM initialization";
- }
- scopedShutdown.disarm();
- return "";
- }
*
* VM initialization. Pass in any options provided on the command line.
* Do not pass in the class name or the options for the class.
*
* Returns 0 on success.
*/
std::string dvmStartup(int argc, const char* const argv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
{
ScopedShutdown scopedShutdown;
assert(gDvm.initializing);
LOGV("VM init args (%d):", argc);
for (int i = 0; i < argc; i++) {
LOGV(" %d: '%s'", i, argv[i]);
}
setCommandLineDefaults();
/*
* Process the option flags (if any).
*/
int cc = processOptions(argc, argv, ignoreUnrecognized);
if (cc != 0) {
if (cc < 0) {
dvmFprintf(stderr, "\n");
usage("dalvikvm");
}
return "syntax error";
}
#if WITH_EXTRA_GC_CHECKS > 1
/* only "portable" interp has the extra goodies */
if (gDvm.executionMode != kExecutionModeInterpPortable) {
LOGI("Switching to 'portable' interpreter for GC checks");
gDvm.executionMode = kExecutionModeInterpPortable;
}
#endif
/* Configure group scheduling capabilities */
if (!access("/dev/cpuctl/tasks", F_OK)) {
LOGV("Using kernel group scheduling");
gDvm.kernelGroupScheduling = 1;
} else {
LOGV("Using kernel scheduler policies");
}
/* configure signal handling */
if (!gDvm.reduceSignals)
blockSignals();
/* verify system page size */
if (sysconf(_SC_PAGESIZE) != SYSTEM_PAGE_SIZE) {
return StringPrintf("expected page size %d, got %d",
SYSTEM_PAGE_SIZE, (int) sysconf(_SC_PAGESIZE));
}
/* mterp setup */
LOGV("Using executionMode %d", gDvm.executionMode);
dvmCheckAsmConstants();
/*
* Initialize components.
*/
dvmQuasiAtomicsStartup();
if (!dvmAllocTrackerStartup()) {
return "dvmAllocTrackerStartup failed";
}
if (!dvmGcStartup()) {
return "dvmGcStartup failed";
}
if (!dvmThreadStartup()) {
return "dvmThreadStartup failed";
}
if (!dvmInlineNativeStartup()) {
return "dvmInlineNativeStartup";
}
if (!dvmRegisterMapStartup()) {
return "dvmRegisterMapStartup failed";
}
if (!dvmInstanceofStartup()) {
return "dvmInstanceofStartup failed";
}
if (!dvmClassStartup()) {
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()) {
return "dvmFindRequiredClassesAndMembers failed";
}
if (!dvmStringInternStartup()) {
return "dvmStringInternStartup failed";
}
if (!dvmNativeStartup()) {
return "dvmNativeStartup failed";
}
if (!dvmInternalNativeStartup()) {
return "dvmInternalNativeStartup failed";
}
if (!dvmJniStartup()) {
return "dvmJniStartup failed";
}
if (!dvmProfilingStartup()) {
return "dvmProfilingStartup failed";
}
/*
* Create a table of methods for which we will substitute an "inline"
* version for performance.
*/
if (!dvmCreateInlineSubsTable()) {
return "dvmCreateInlineSubsTable failed";
}
/*
* Miscellaneous class library validation.
*/
if (!dvmValidateBoxClasses()) {
return "dvmValidateBoxClasses failed";
}
/*
* Do the last bits of Thread struct initialization we need to allow
* JNI calls to work.
*/
if (!dvmPrepMainForJni(pEnv)) {
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)) {
return "couldn't register system natives";
}
/*
* Do some "late" initialization for the memory allocator. This may
* allocate storage and initialize classes.
*/
if (!dvmCreateStockExceptions()) {
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()) {
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)
{
LOGW("Warning: tracked references remain post-initialization");
dvmDumpReferenceTable(&dvmThreadSelf()->internalLocalRefTable, "MAIN");
}
/* general debugging setup */
if (!dvmDebuggerStartup()) {
return "dvmDebuggerStartup failed";
}
if (!dvmGcStartupClasses()) {
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";
}
}
#ifndef NDEBUG
if (!dvmTestHash())
LOGE("dvmTestHash FAILED");
if (false /*noisy!*/ && !dvmTestIndirectRefTable())
LOGE("dvmTestIndirectRefTable FAILED");
#endif
if (dvmCheckException(dvmThreadSelf())) {
dvmLogExceptionStackTrace();
return "Exception pending at end of VM initialization";
}
scopedShutdown.disarm();
return "";
}代码真长。寻找其中最具价值的部分
插入代码:
- if (!dvmAllocTrackerStartup()) {
- return "dvmAllocTrackerStartup failed";
- }
- if (!dvmGcStartup()) {
- return "dvmGcStartup failed";
- }
- if (!dvmThreadStartup()) {
- return "dvmThreadStartup failed";
- }
- if (!dvmInlineNativeStartup()) {
- return "dvmInlineNativeStartup";
- }
- if (!dvmRegisterMapStartup()) {
- return "dvmRegisterMapStartup failed";
- }
- if (!dvmInstanceofStartup()) {
- return "dvmInstanceofStartup failed";
- }
- if (!dvmClassStartup()) {
- return "dvmClassStartup failed";
- }
if (!dvmAllocTrackerStartup()) {
return "dvmAllocTrackerStartup failed";
}
if (!dvmGcStartup()) {
return "dvmGcStartup failed";
}
if (!dvmThreadStartup()) {
return "dvmThreadStartup failed";
}
if (!dvmInlineNativeStartup()) {
return "dvmInlineNativeStartup";
}
if (!dvmRegisterMapStartup()) {
return "dvmRegisterMapStartup failed";
}
if (!dvmInstanceofStartup()) {
return "dvmInstanceofStartup failed";
}
if (!dvmClassStartup()) {
return "dvmClassStartup failed";
}经分析,这些都没有建立gc线程,gc线程的建立是在如下方法:
- dvmInitAfterZygote
dvmInitAfterZygote
由于跟得太深,东西很多,就不一一列举。仅仅跟一下dvmClassStartup,最终调用到了dalvik/vm/oo/Class.cpp中的方法:
- /*
- * Initialize the bootstrap class loader.
- *
- * Call this after the bootclasspath string has been finalized.
- */
- bool dvmClassStartup()
- {
- /* make this a requirement -- don't currently support dirs in path */
- if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
- LOGE("ERROR: must specify non-'.' bootclasspath");
- return false;
- }
- gDvm.loadedClasses =
- dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
- gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
- if (gDvm.pBootLoaderAlloc == NULL)
- return false;
- if (false) {
- linearAllocTests();
- exit(0);
- }
- /*
- * Class serial number. We start with a high value to make it distinct
- * in binary dumps (e.g. hprof).
- */
- gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
- /*
- * Set up the table we'll use for tracking initiating loaders for
- * early classes.
- * If it's NULL, we just fall back to the InitiatingLoaderList in the
- * ClassObject, so it's not fatal to fail this allocation.
- */
- gDvm.initiatingLoaderList = (InitiatingLoaderList*)
- calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
- /*
- * Create the initial classes. These are the first objects constructed
- * within the nascent VM.
- */
- if (!createInitialClasses()) {
- return false;
- }
- /*
- * Process the bootstrap class path. This means opening the specified
- * DEX or Jar files and possibly running them through the optimizer.
- */
- assert(gDvm.bootClassPath == NULL);
- processClassPath(gDvm.bootClassPathStr, true);
- if (gDvm.bootClassPath == NULL)
- return false;
- return true;
- }
/*
* Initialize the bootstrap class loader.
*
* Call this after the bootclasspath string has been finalized.
*/
bool dvmClassStartup()
{
/* make this a requirement -- don't currently support dirs in path */
if (strcmp(gDvm.bootClassPathStr, ".") == 0) {
LOGE("ERROR: must specify non-'.' bootclasspath");
return false;
}
gDvm.loadedClasses =
dvmHashTableCreate(256, (HashFreeFunc) dvmFreeClassInnards);
gDvm.pBootLoaderAlloc = dvmLinearAllocCreate(NULL);
if (gDvm.pBootLoaderAlloc == NULL)
return false;
if (false) {
linearAllocTests();
exit(0);
}
/*
* Class serial number. We start with a high value to make it distinct
* in binary dumps (e.g. hprof).
*/
gDvm.classSerialNumber = INITIAL_CLASS_SERIAL_NUMBER;
/*
* Set up the table we'll use for tracking initiating loaders for
* early classes.
* If it's NULL, we just fall back to the InitiatingLoaderList in the
* ClassObject, so it's not fatal to fail this allocation.
*/
gDvm.initiatingLoaderList = (InitiatingLoaderList*)
calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
/*
* Create the initial classes. These are the first objects constructed
* within the nascent VM.
*/
if (!createInitialClasses()) {
return false;
}
/*
* Process the bootstrap class path. This means opening the specified
* DEX or Jar files and possibly running them through the optimizer.
*/
assert(gDvm.bootClassPath == NULL);
processClassPath(gDvm.bootClassPathStr, true);
if (gDvm.bootClassPath == NULL)
return false;
return true;
}根据注释,Initialize the bootstrap class loader.
这个函数告诉我们,他建立了boottrap classloader。createInitialClasses加载了9大基本类型。而后的processClassPath则建立了基本的classloader。分析过后,比较失望。可能是为后续的boottrapclassloader做一些前期准备工作。
startVM就到这里。
好吧。现在又回到了最初的App_main.cpp中。进入了com.android.internal.os.ZygoteInit.java的main
- public static void main(String argv[]) {
- try {
- // Start profiling the zygote initialization.
- SamplingProfilerIntegration.start();
- registerZygoteSocket();
- EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
- SystemClock.uptimeMillis());
- preload();
- EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
- SystemClock.uptimeMillis());
- // Finish profiling the zygote initialization.
- SamplingProfilerIntegration.writeZygoteSnapshot();
- // Do an initial gc to clean up after startup
- gc();
- // If requested, start system server directly from Zygote
- if (argv.length != 2) {
- throw new RuntimeException(argv[0] + USAGE_STRING);
- }
- if (argv[1].equals("start-system-server")) {
- startSystemServer();
- } else if (!argv[1].equals("")) {
- throw new RuntimeException(argv[0] + USAGE_STRING);
- }
- Log.i(TAG, "Accepting command socket connections");
- if (ZYGOTE_FORK_MODE) {
- runForkMode();
- } else {
- runSelectLoopMode();
- }
- closeServerSocket();
- } catch (MethodAndArgsCaller caller) {
- caller.run();
- } catch (RuntimeException ex) {
- Log.e(TAG, "Zygote died with exception", ex);
- closeServerSocket();
- throw ex;
- }
- }
public static void main(String argv[]) {
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
registerZygoteSocket();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
// Finish profiling the zygote initialization.
SamplingProfilerIntegration.writeZygoteSnapshot();
// Do an initial gc to clean up after startup
gc();
// If requested, start system server directly from Zygote
if (argv.length != 2) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
if (argv[1].equals("start-system-server")) {
startSystemServer();
} else if (!argv[1].equals("")) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
Log.i(TAG, "Accepting command socket connections");
if (ZYGOTE_FORK_MODE) {
runForkMode();
} else {
runSelectLoopMode();
}
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
回过头来继续看一下ZygoteInit.java这个类是如何初始化的,看如下代码:
- /*
- * Create a new JNIEnv struct and add it to the VM's list.
- *
- * "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)
- // LOGI("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);
- /* 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)
- // LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
- return (JNIEnv*) newEnv;
- }
/*
* Create a new JNIEnv struct and add it to the VM's list.
*
* "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)
// LOGI("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);
/* 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)
// LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
return (JNIEnv*) newEnv;
}
最重要的是gNativeInterface 我们看定义,太长了,我们找到其中的FindClass。
- static jclass FindClass(JNIEnv* env, const char* name) {
- ScopedJniThreadState ts(env);
- const Method* thisMethod = dvmGetCurrentJNIMethod();
- assert(thisMethod != NULL);
- Object* loader;
- Object* trackedLoader = NULL;
- if (ts.self()->classLoaderOverride != NULL) {
- /* hack for JNI_OnLoad */
- assert(strcmp(thisMethod->name, "nativeLoad") == 0);
- loader = ts.self()->classLoaderOverride;
- } else if (thisMethod == gDvm.methDalvikSystemNativeStart_main ||
- thisMethod == gDvm.methDalvikSystemNativeStart_run) {
- /* start point of invocation interface */
- if (!gDvm.initializing) {
- loader = trackedLoader = dvmGetSystemClassLoader();
- } else {
- loader = NULL;
- }
- } else {
- loader = thisMethod->clazz->classLoader;
- }
- char* descriptor = dvmNameToDescriptor(name);
- if (descriptor == NULL) {
- return NULL;
- }
- ClassObject* clazz = dvmFindClassNoInit(descriptor, loader);
- free(descriptor);
- jclass jclazz = (jclass) addLocalReference(ts.self(), (Object*) clazz);
- dvmReleaseTrackedAlloc(trackedLoader, ts.self());
- return jclazz;
- }
static jclass FindClass(JNIEnv* env, const char* name) {
ScopedJniThreadState ts(env);
const Method* thisMethod = dvmGetCurrentJNIMethod();
assert(thisMethod != NULL);
Object* loader;
Object* trackedLoader = NULL;
if (ts.self()->classLoaderOverride != NULL) {
/* hack for JNI_OnLoad */
assert(strcmp(thisMethod->name, "nativeLoad") == 0);
loader = ts.self()->classLoaderOverride;
} else if (thisMethod == gDvm.methDalvikSystemNativeStart_main ||
thisMethod == gDvm.methDalvikSystemNativeStart_run) {
/* start point of invocation interface */
if (!gDvm.initializing) {
loader = trackedLoader = dvmGetSystemClassLoader();
} else {
loader = NULL;
}
} else {
loader = thisMethod->clazz->classLoader;
}
char* descriptor = dvmNameToDescriptor(name);
if (descriptor == NULL) {
return NULL;
}
ClassObject* clazz = dvmFindClassNoInit(descriptor, loader);
free(descriptor);
jclass jclazz = (jclass) addLocalReference(ts.self(), (Object*) clazz);
dvmReleaseTrackedAlloc(trackedLoader, ts.self());
return jclazz;
}
在dalvik/vm/Init.cpp中的方法对gVM的bootpath进行了初始化:
- static void setCommandLineDefaults()
- {
- const char* envStr = getenv("CLASSPATH");
- if (envStr != NULL) {
- gDvm.classPathStr = strdup(envStr);
- } else {
- gDvm.classPathStr = strdup(".");
- }
- envStr = getenv("BOOTCLASSPATH");
- if (envStr != NULL) {
- gDvm.bootClassPathStr = strdup(envStr);
- } else {
- gDvm.bootClassPathStr = strdup(".");
- }
- gDvm.properties = new std::vector<std::string>();
- /* Defaults overridden by -Xms and -Xmx.
- * TODO: base these on a system or application-specific default
- */
- gDvm.heapStartingSize = 2 * 1024 * 1024; // Spec says 16MB; too big for us.
- gDvm.heapMaximumSize = 16 * 1024 * 1024; // Spec says 75% physical mem
- gDvm.heapGrowthLimit = 0; // 0 means no growth limit
- gDvm.stackSize = kDefaultStackSize;
- gDvm.concurrentMarkSweep = true;
- /* gDvm.jdwpSuspend = true; */
static void setCommandLineDefaults()
{
const char* envStr = getenv("CLASSPATH");
if (envStr != NULL) {
gDvm.classPathStr = strdup(envStr);
} else {
gDvm.classPathStr = strdup(".");
}
envStr = getenv("BOOTCLASSPATH");
if (envStr != NULL) {
gDvm.bootClassPathStr = strdup(envStr);
} else {
gDvm.bootClassPathStr = strdup(".");
}
gDvm.properties = new std::vector<std::string>();
/* Defaults overridden by -Xms and -Xmx.
* TODO: base these on a system or application-specific default
*/
gDvm.heapStartingSize = 2 * 1024 * 1024; // Spec says 16MB; too big for us.
gDvm.heapMaximumSize = 16 * 1024 * 1024; // Spec says 75% physical mem
gDvm.heapGrowthLimit = 0; // 0 means no growth limit
gDvm.stackSize = kDefaultStackSize;
gDvm.concurrentMarkSweep = true;
/* gDvm.jdwpSuspend = true; */
现在明白了,在init.rc中指定的BOOTCLASSPATH赋值给了gDvm.bootClassPathStr 。
而下面这个地方,则对FindClass进行了初始化。还是在dalvik/vm/Init.cpp中
- */
- f (!dvmFindRequiredClassesAndMembers()) {
- return "dvmFindRequiredClassesAndMembers failed";
*/
if (!dvmFindRequiredClassesAndMembers()) {
return "dvmFindRequiredClassesAndMembers failed";
}initDirectMethodReferences 把方法gDvm.methDalvikSystemNativeStart_main与NativeStart进行了对应。
- { &gDvm.methDalvikSystemNativeStart_main, "Ldalvik/system/NativeStart;", "main", "([Ljava/lang/String;)V" },
{ &gDvm.methDalvikSystemNativeStart_main, "Ldalvik/system/NativeStart;", "main", "([Ljava/lang/String;)V" },
主要分析FindClass方法:
nativeLoad标示从System.loadlibrary加载。那么Zygote的dvmGetCurrentJNIMethod是哪个呢。我猜测,这个一定是没有的,也就是里面的成员变量全为空。哈哈。这意味着,最终调用到了
dvmFindClassNoInit方法中。
然后是ClassObject* dvmFindSystemClassNoInit(const char* descriptor)
{
return findClassNoInit(descriptor, NULL, NULL);
}
然后是findClassNoInitZygoteInit这个类根本是没有ClassLoader的。而是直接从包里面查找得到的。
进入ZygoteInit后。就是java代码了。