本文將帶你了解Android應用開發之Android 系統啟動原理(art 虛擬機),希望本文對大家學Android有所幫助。
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 部分代碼:
?1234567891011121314// 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 部分代碼:
?12345678910if (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 部分代碼:
?123if (startVm(&mJavaVM, &env, zygote) ? != 0) {????return;}
在 AndroidRuntime 的 start 方法中,調用了 startVm,這個方法,這個方法才是真正的去開啟虛擬機。手機啟動的時候只是開啟 Linux 系統,當執行到這里的時候,Linux 系統開啟安卓運行的虛擬機。
startVm 部分代碼:
?1234567891011/*?* 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 :
?1234567891011121314151617181920212223242526272829// ? 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 *
?1234567// Every thread may have an associated ? JNI environmentJNIEnvExt* jni_env_;?// JNI methodsJNIEnvExt* 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());????Handleclass_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,????????????????????????????Handleklass) {??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 進行初始化。
本文由職坐標整理并發布,希望對同學們有所幫助。了解更多詳情請關注職坐標移動開發之Android頻道!