1.虛擬機啟動過程
Android系統(tǒng)在啟動的時候,會創(chuàng)建一個Zygote進程,而Zygote進程會創(chuàng)建一個虛擬機實例绘迁,并加載java運行庫等等嘴秸。當(dāng)有新的進程要啟動時毁欣,系統(tǒng)會從Zygote中fork一個新的進程出來,并且將Zygote的虛擬機實例復(fù)制過去岳掐。
首先要從Zygote系統(tǒng)的啟動說起凭疮。Zygote進程源碼在frameworks\base\cmds\app_process\app_main.cpp里。zygote啟動腳本:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
app_process后的內(nèi)容為命令行參數(shù)
下面看下這個app_main.cpp的main函數(shù):
int main(int argc, char* const argv[])
{
...
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...
//從命令行獲取進程啟動參數(shù)
...
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} 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;
}
}
這個函數(shù)主要做的事情就是串述,新建一個AppRuntime實例执解,然后從命令行參數(shù)中獲取進程啟動的參數(shù),然后調(diào)用AppRuntime類的start纲酗。
AppRuntime類定義在App_main.cpp文件中衰腌,繼承自AndroidRuntime類新蟆。而AppRuntime沒有定義start方法,所以這個start方法屬于它的父類AndroidRuntime右蕊。
AndroidRuntime定義在frameworks\base\include\android_runtime\AndroidRuntime.cpp中琼稻。
/*
* 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 Vector<String8>& options, bool zygote)
{
...
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 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;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 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) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
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 {
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");
}
第一步
啟動虛擬機startVm,并把啟動的虛擬機實例保存在全局變量mJavaVM里
/*
* Start the Dalvik Virtual Machine.
*
* Various arguments, most determined by system properties, are passed in.
* The "mOptions" vector is updated.
*
* CAUTION: when adding options in here, be careful not to put the
* char buffer inside a nested scope. Adding the buffer to the
* options using mOptions.add() does not copy the buffer, so if the
* buffer goes out of scope the option may be overwritten. It's best
* to put the buffer at the top of the function so that it is more
* unlikely that someone will surround it in a scope at a later time
* and thus introduce a bug.
*
* Returns 0 on success.
*/
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
JavaVMInitArgs initArgs;
...
中間一堆虛擬機配置信息
...
...
initArgs.version = JNI_VERSION_1_4;
initArgs.options = mOptions.editArray();
initArgs.nOptions = mOptions.size();
initArgs.ignoreUnrecognized = JNI_FALSE;
/*
* 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");
return -1;
}
return 0;
}
startVm中調(diào)用了jni.h中的JNI_CreateJavaVM方法饶囚。
** 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;
}
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++) {
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);
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;
}
第二步
調(diào)用startReg方法帕翻,注冊一些android核心類的jni方法
static const RegJNIRec gRegJNI[] = {
...
REG_JNI(register_android_view_InputChannel),
REG_JNI(register_android_view_InputEventReceiver),
REG_JNI(register_android_view_InputEventSender),
REG_JNI(register_android_view_InputQueue),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
REG_JNI(register_android_view_PointerIcon),
REG_JNI(register_android_view_VelocityTracker),
...
};
/*
* Register android native functions with the VM.
*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
ATRACE_NAME("RegisterAndroidNatives");
/*
* 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;
}
gRegJNI數(shù)組里保存了一堆注冊android核心類jni方法的函數(shù)指針,在startReg方法中調(diào)用了register_jni_procs方法萝风,來注冊jni方法熊咽。
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
for (size_t i = 0; i < count; i++) {
if (array[i].mProc(env) < 0) {
#ifndef NDEBUG
ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
return -1;
}
}
return 0;
}
register_jni_procs方法,把gRegJNI數(shù)組遍歷了一遍闹丐,然后把函數(shù)指針一次執(zhí)行了一遍横殴。
第三步
找到main方法,然后在虛擬機實例中運行起來卿拴,如果main方法不返回衫仑,那么虛擬機就不會退出。
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
從start方法的這段代碼可以知道堕花,當(dāng)初次啟動zygote進程時文狱,入口類為ZygoteInit
public static void main(String argv[]) {
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
try {
...
registerZygoteSocket(socketName);
...
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
main方法中,做了一些初始化的工作缘挽,啟動了系統(tǒng)服務(wù)瞄崇,然后注冊了一個本地socket,用于其他的系統(tǒng)服務(wù)和zygote進程通信壕曼,在runSelectLoop方法中苏研,開啟socket循環(huán)。只要這個循環(huán)不退出腮郊,那么這個main方法就不會結(jié)束摹蘑。
第四步
最后一步就是關(guān)閉虛擬機了。當(dāng)?shù)谌降膍ain函數(shù)結(jié)束時轧飞,虛擬機將會關(guān)閉衅鹿,虛擬機的實例被銷毀。