Android 系统启动原理(art 虚拟机)
android 启动原理(art )
一、虚拟机的启动
android 是一个 linux 的虚拟机,当虚拟机启动的时候,会执行手机根目录下的 init.rc(实际上就是 .sh 文件) 这个可执行文件。
在 init.rc 中,有一行 on init 执行命令。这是调用 init.rc 同级文件 init ,init 是所有安卓手机的入口执行文件,无法打开查看,是乱码。
xpose 的强大功能,就是对 init 进行 hook,然后修改。但是替换 init 这个文件是需要 root 权限的,所以使用 xpose 这个框架,是需要进行 root 的。
1.init
inti 文件的源码是在 \system\core\init 这个文件夹下,会把里面所有的东西编译成 init 这个可执行文件,各个手机厂商会对这块文件进行修改。
init 的唯一入口是改文件夹下的 init.cpp 这个文件,里面有一个 main 函数,处理环境变量,开启服务,渲染等。
main 部分代码:
// if we're in the kernel domain, re-exec init to transition to the init domain now // that the selinux policy has been loaded. if (is_first_stage) { if (restorecon("/init") == -1) { error("restorecon failed: %s\n", strerror(errno)); security_failure(); } char* path = argv[0]; char* args[] = { path, const_cast("--second-stage"), nullptr }; if (execv(path, args) == -1) { error("execv(\"%s\") failed: %s\n", path, strerror(errno)); security_failure(); } }
代码中的 path 是指系统定义好的一些环境变量,这些路径是 \frameworks\base\cmds 下的所有东西。
所以在这里是判断是否是第一次启动,如果是第一次启动,则会执行 \frameworks\base\cmds 下所有的可执行文件,包括开启虚拟机的文件 app_process。
2.app_process 源码
\frameworks\base\cmds\app_process 下有个 app_main.cpp 文件,里面就是 app_process 源码。
app_process 部分代码:
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; }
在 app_main 里面的 main 方法最后,调用了 runtime.start(“com.android.internal.os.zygoteinit”, args, zygote);
点击查看 run 是第一 appruntime。
所以 app_process 调用了 com.android.internal.os.zygoteinit 这个类,这是第一个被调用的 java 类。对应源码位置是 \frameworks\base\core\java\com\android\internal\os
3.androidruntime
appruntime 继承于 androidruntime,androidruntime 位于\frameworks\base\core\jni。
start 部分代码:
if (startvm(&mjavavm, &env, zygote) != 0) { return; }
在 androidruntime 的 start 方法中,调用了 startvm,这个方法,这个方法才是真正的去开启虚拟机。手机启动的时候只是开启 linux 系统,当执行到这里的时候,linux 系统开启安卓运行的虚拟机。
startvm 部分代码:
/* * 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; }
在 startvm 末尾调用 jni_createjavavm,去创建一个虚拟机。
4.jni_createjavavm
jni_createjavavm 方法位于 \art\runtime\jni_internal.cc 文件中。
jni_createjavavm :
// jni invocation interface. extern "c" jint jni_createjavavm(javavm** p_vm, jnienv** p_env, void* vm_args) { const javavminitargs* args = static_cast(vm_args); if (isbadjniversion(args->version)) { log(error) << "bad jni version passed to createjavavm: " << args->version; return jni_eversion; } runtime::options options; for (int i = 0; i < args->noptions; ++i) { javavmoption* option = &args->options[i]; options.push_back(std::make_pair(std::string(option->optionstring), option->extrainfo)); } bool ignore_unrecognized = args->ignoreunrecognized; if (!runtime::create(options, ignore_unrecognized)) { return jni_err; } runtime* runtime = runtime::current(); bool started = runtime->start(); if (!started) { delete thread::current()->getjnienv(); delete runtime->getjavavm(); log(warning) << "createjavavm failed"; return jni_err; } *p_env = thread::current()->getjnienv(); *p_vm = runtime->getjavavm(); return jni_ok; }
其中最主要的最后两行代码,实例化了 p_env 和 p_vm ,p_env 就是我们编写 jni 方法的第一个参数 jnienv *env ,p_vm 就是虚拟机。
//jnienv *env 实例化
*p_env = thread::current()->getjnienv();
//实例化虚拟机的地方
*p_vm = runtime->getjavavm();
注:虚拟机在 linux 就是一个结构体的方式保存着。
5.p_env
getjnienv() 这个函数定义在文件 \art\runtime 下的 thread.h 中。
* thread.h *
// every thread may have an associated jni environment jnienvext* jni_env_; // jni methods jnienvext* getjnienv() const { return jni_env_; }
jni 方法的第一个参数是 jnienv,jnienv 是一个接口, jnienvext 是 jnienv子类。
二、加载 java 文件
在 \frameworks\base\core\jni\androidruntime 中继续往下,会发现加载 java 类,实际上是调用 env->findclass(slashclassname) 进行加载的。(java 中 双亲委托机制 classloader 进行加载 java 文件,最底层的实现也是使用 findclass 这个方法)
1.findclass
findclass 是在 libnativehelper\include\nativehelper\jni.h 中,
jni.h 下 findclass :
jclass findclass(const char* name) { return functions->findclass(this, name); }
这里的 functions 是 jninativeinterface,最终调用的是 \art\runtime\jni_internal.cc 下的 findclass 。
\jni_internal.cc 下 findclass:
static jclass findclass(jnienv* env, const char* name) { check_non_null_argument(name); runtime* runtime = runtime::current(); classlinker* class_linker = runtime->getclasslinker(); std::string descriptor(normalizejniclassdescriptor(name)); scopedobjectaccess soa(env); mirror::class* c = nullptr; //判断虚拟机是否开启 if (runtime->isstarted()) { stackhandlescope<1> hs(soa.self()); handle class_loader(hs.newhandle(getclassloader(soa))); c = class_linker->findclass(soa.self(), descriptor.c_str(), class_loader); } else { //还没开启虚拟机,即加载的是系统的类 c = class_linker->findsystemclass(soa.self(), descriptor.c_str()); } return soa.addlocalreference(c); }
最终程序调用到 class_linker 的 findclass 方法进行加载类。
2. class_linker 的 findclass
class_linker 所在目录 \art\runtime 下有一个 class_linker.cc 文件,找到里面的 findclass 方法。
findclass 部分代码:
if (pair.second != nullptr) { return defineclass(self, descriptor, hash, scopednullhandle(), *pair.first, *pair.second); }
在这边调用了 defineclass。
defineclass 部分代码:
// add the newly loaded class to the loaded classes table. mirror::class* existing = insertclass(descriptor, klass.get(), hash); if (existing != nullptr) { // we failed to insert because we raced with another thread. calling ensureresolved may cause // this thread to block. return ensureresolved(self, descriptor, existing); } // load the fields and other things after we are inserted in the table. this is so that we don't // end up allocating unfree-able linear alloc resources and then lose the race condition. the // other reason is that the field roots are only visited from the class table. so we need to be // inserted before we allocate / fill in these fields. loadclass(self, dex_file, dex_class_def, klass);
这是调用了两个比较重要的方法, insertclass 和 loadclass。
insertclass(descriptor, klass.get(), hash); 有一个参数是 hash,这样会把类进行缓存,在 defineclass 执行 insertclass 之前,会先进行这个判断,如果已经加载的就不再进行加载。
loadclass(self, dex_file, dex_class_def, klass); 是真正的去进行加载 class。
loadclass:
void classlinker::loadclass(thread* self, const dexfile& dex_file, const dexfile::classdef& dex_class_def, handle klass) { const uint8_t* class_data = dex_file.getclassdata(dex_class_def); if (class_data == nullptr) { return; // no fields or methods - for example a marker interface } bool has_oat_class = false; if (runtime::current()->isstarted() && !runtime::current()->isaotcompiler()) { oatfile::oatclass oat_class = findoatclass(dex_file, klass->getdexclassdefindex(), &has_oat_class); if (has_oat_class) { loadclassmembers(self, dex_file, class_data, klass, &oat_class); } } if (!has_oat_class) { loadclassmembers(self, dex_file, class_data, klass, nullptr); } }
最开始是通过 dexfile 去获取到 classdata。因为在类还没加载的时候,class 是以 dex格式 存在在 磁盘 文件下,这时候需要先把 dex 转为 class,再把 class 加载到内存中。
然后通过 loadclassmembers 进行加载类的信息,分配内存。loadclassmembers 中分别对 artfield 和 artmethod 进行初始化。