Android 5.1 Lollipop的Zygote分析——上篇
整體介紹
因為公司最近的項目,頻繁的使用了xposed框架。作為一種Hook技術,xposed框架主要是對Android系統中的app_process程序做了手腳。為了弄清楚xposed框架背后的原理,那么肯定要分析app_process它的原理嘍。
app_process是在程序執行期間將其名字修改為zygote的。zygote是受精卵的意思,主要作用就是進行細胞分裂嘛,是Android系統執行APK程序的核心服務。zygote進程首先加載啟動ART虛擬機,然后在加載一些系統核心類和資源,這些都是其他APK程序都可能要用到的資源。最后zygote進程進入監聽狀態。一旦Android上層有創建新APK進程的需求,zygote進程便會為其分裂出新的進程。這個APK新進程,一開始便擁有了ART虛擬機和zygote預先加載的各種系統類和資源,能大大加速apk應用的啟動,同時也能節省很大的內存開支。
這里解釋一下,為什么zygote進程要預先加載系統資源。zygote進程實際上是利用fork分裂出新的進程的,linux內核采用了寫時復制技術:內核只為新生成的子進程創建虛擬空間結構,它們來自于父進程的虛擬地址結構,但是不為子進程的虛擬地址分配物理內存,它們共享父進程的物理空間,當父子進程中有更改相應段的行為發生時,再為子進程相應的段分配物理空間。
通常來說,在利用fork創建出的子進程中會調用exec系統調用,去執行新的其他程序。在fork之后exec之前兩個進程用的是相同的物理空間(內存區),子進程的代碼段、數據段、堆棧都是指向父進程的物理空間,也就是說,兩者的虛擬空間不同,但其對應的物理空間是同一個。當父子進程中有更改相應段的行為發生時,再為子進程相應的段分配物理空間,如果不是因為exec,內核會給子進程的數據段、堆棧段分配相應的物理空間(至此兩者有各自的進程空間,互不影響),而代碼段繼續共享父進程的物理空間(兩者的代碼完全相同)。而如果是因為exec,由于兩者執行的代碼不同,子進程的代碼段也會分配單獨的物理空間。
因為Android上層APK程序不會去修改zygote進程預先加載的系統類和系統資源,所以就不需要為每個APK進程重新分配這些資源,所有APK程序都共用在zygote預先加載的這些資源,所以性能自然也就提高了。
zygote在init.rc中的配置
app_process進程,是在init.rc中通過引入下面的文件被配置的:
| 1 | import /init.${ro.zygote}.rc |
在Android源碼/system/core/rootdir中,有如下圖所示的四個與zygote相關的文件
可見,究竟是將這個四個文件中的哪一個導入到init.rc中是由ro.zygote這個只讀屬性決定的。
google在Android 5.0中加入了對64位CPU的支持,這些zygote相關的rc文件就是與32位和64位CPU有關系。如果ro.zygote為zygote32,那么說明只支持32位程序;如果是zygote64,那么只支持64位程序;如果是zygote32_64,說明支持32位程序為主,兼容64位程序;如果是zygote64_32說明支持64位程序為主,兼容32位程序。
下面是zygote64_32.rc中的內容:
| 1 2 3 4 5 6 7 8 9 10 11 12 | service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote 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 service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary class main socket zygote_secondary stream 660 root system onrestart restart zygote |
其中app_process64 和app_process32 就是zygote進程的可執行程序,啟動后會改名成zygote。
顧名思義,zygote_secondary即app_process32是一個運行在32位的進程,它所連接的庫也都是32位的。而zygote就是運行在64位的進程,它所連接的庫都是64位的。在不考慮有32/64兼容庫的情況下,一個進程如果要正確運行,就必須從可執行程序入口開始到所有使用的庫都保持32/64位的一致性。因為zygote進程是所有第三方應用程序的父進程,所以可以認為,如果應用程序是32位的,那沒他的父進程也肯定是32位,換句話說,如果需要啟動某個32位的應用,那么肯定是通過32位的zygote進程fork出來的。
zygote代碼分析
app_process相關的代碼在:Android源碼/frameworks/base/cmds/app_process中。都是c++代碼,那么就好辦了,找到main函數,也就找到了zygote進程的入口了。
main函數在app_main.cpp中,另外傳入的參數為 -Xzygote /system/bin –zygote –socket-name=zygote (或者zygote_second)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | int main(int argc, char* const argv[]) { ............................................................................ AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));//AppRuntime繼承自AndroidRuntime類 // Process command line arguments // ignore argv[0] argc--; argv++; .............................................................................. int i; for (i = 0; i < argc; i++) { if (argv[i][0] != '-') { break; } if (argv[i][1] == '-' && argv[i][2] == 0) { ++i; // Skip --. break; } runtime.addOption(strdup(argv[i]));//-------傳遞的參數是 -Xzygote,結束后i=1 } // Parse runtime arguments. Stop at first unrecognized option. bool zygote = false; bool startSystemServer = false; bool application = false; String8 niceName; String8 className; ++i; // Skip unused "parent dir" argument. --------------------i=2 while (i < argc) { const char* arg = argv[i++]; if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = ZYGOTE_NICE_NAME; } 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.setTo(arg + 12); } else if (strncmp(arg, "--", 2) != 0) { className.setTo(arg); break; } else { --i; break; } } ............................................................................. if (!niceName.isEmpty()) { //niceNmae為 zygote runtime.setArgv0(niceName.string()); set_process_name(niceName.string()); } if (zygote) { runtime.start("com.android.internal.os.ZygoteInit", args);//---------很關鍵的函數,在ANdroidRuntime類中實現,args為start-system-server } else if (className) { runtime.start("com.android.internal.os.RuntimeInit", args); } 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; } } |
main函數流程還是比較清晰的,創建AppRuntime的對象runtime,然后就是解析參數,決定下一步要做什么。
傳遞的參數包含了–zygote參數和–start-system-server參數,zygote和startSystemServer都為true,所以main函數最后執行的是
| 1 | runtime.start("com.android.internal.os.ZygoteInit", args)// args為start-system-server |
AppRuntime的start函數
AppRuntime類繼承于AndroidRuntime類,自身沒有實現start函數,所以main()函數中調用的runtime.start()肯定調用的是其父類是AndroidRuntime的start()函數。
AndroidRuntime對應的代碼位于frameworks/base/core/jni/AndroidRuntime.cpp,
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | void AndroidRuntime::start(const char* className, const Vector<String8>& options) { ALOGD(">>>>>> START %s uid %d <<<<<<\n", className != NULL ? className : "(unknown)", getuid()); ............................................................................................. static const String8 startSystemServer("start-system-server"); /* * 'startSystemServer == true' means runtime is obsolete and not run from * init.rc anymore, so we print out the boot start event here. */ for (size_t i = 0; i < options.size(); ++i) { if (options[i] == startSystemServer) { /* 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");//-------------如果沒有設置ANDROID_ROOT環境變量,就將其設置為/system 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); /* start the virtual machine */ JniInvocation jni_invocation;//------------------初始化JNI環境,里面做了很重要的工作,加載對應的虛擬機so庫 jni_invocation.Init(NULL); JNIEnv* env; if (startVm(&mJavaVM, &env) != 0) { //------------------啟動虛擬機 return; } onVmCreated(env);//-----------------該方法是一個虛函數,是由其子類負責實現的 /* * Register android functions. */ if (startReg(env) < 0) {----------------------為Android核心類在虛擬機中為其注冊所需的的JNI函數 ALOGE("Unable to register all android natives\n"); return; } ................................................................................................................. char* slashClassName = toSlashClassName(className);// classname為傳遞過來的參數com.android.internal.os.ZygoteInit,該函數作用是將其轉換為com/android/internal/os/ZygoteInit 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 {找className指定的class的main函數,并以options為參數,調用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"); } |
AndroidRunTime.start函數主要完成了三項工作:
1.啟動虛擬機 首先初始化JNI環境:
| 1 2 | JniInvocation jni_invocation; jni_invocation.Init(NULL); |
這部分代碼實現在Android源碼/libnativehelper/JniInvocation.cpp中。這里有一個很重要的步驟,就是加載虛擬機實現so庫。Android4.4開始引入了ART虛擬機,但是默認的還是dalvik虛擬機。但是從Andoroid 5.0開始就只有ART虛擬機了。
| 1 2 3 4 5 6 | #ifdef HAVE_ANDROID_OS static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2"; static const char* kDebuggableSystemProperty = "ro.debuggable"; static const char* kDebuggableFallback = "0"; // Not debuggable. #endif static const char* kLibraryFallback = "libart.so"; |
我們使用的就是Android系統,所以HAVE_ANDROID_OS必然是設置了,persist.sys.dalvik.vm.lib.2”記錄了使用的是哪種虛擬機。在Android 5.0以上版本的設備中,執行如下命令:
| 1 2 | root@generic_x86_64:/ # getprop persist.sys.dalvik.vm.lib.2 libart.so |
可知加載的庫就是ART虛擬機的實現庫libart.so。
接下來是調用AndroidRuntime的startVm方法啟動ART虛擬機。
最后是調用AndroidRuntime子類,也就是app_main.cpp中定義的AppRuntime類中的onVmCreated函數。
2.向虛擬機注冊需要的Native函數基本Android的每個模塊都有一些native實現需要和Java代碼關聯起來,前面也說過了事先注冊能夠提高性能。
調用AndroidRuntime類中的startReg注冊JNI函數。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /*static*/ int AndroidRuntime::startReg(JNIEnv* env) { ...................................................................................... 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是一個數組
| 1 2 3 4 5 6 7 | static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_os_RuntimeInit), REG_JNI(register_android_os_SystemClock), REG_JNI(register_android_util_EventLog), REG_JNI(register_android_util_Log), ........................... } |
宏REG——JNI
| 1 2 3 4 | #define REG_JNI(name) { name } struct RegJNIRec { int (*mProc)(JNIEnv*); }; |
注冊jni函數的動作很簡單,只是在遍歷array數組,并嘗試回調每個數組項的mProc回調函數.注冊JNI其實也很好理解,主要寫過JNI程序的都知道定義的JNI函數是給某個上層JAVA類調用的嘛,注冊的意思就是注冊給哪個類了,哪個類才能調用這些JNI函數。
3.找className指定的class的main函數,并以options為參數,調用main函數AppRuntime的start()最后會加載Java層次的ZygoteInit類(com.android.internal.os.ZygoteInit),并利用JNI技術的CallStaticVoidMethod()調用其靜態的main()函數,從這一步開始,Android系統從native層進入到了JAVA的世界了。
原文地址:?http://www.iloveandroid.net/2015/09/21/Zygote_1/
總結
以上是生活随笔為你收集整理的Android 5.1 Lollipop的Zygote分析——上篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android客户端内置内存工具进行崩溃
- 下一篇: Android 5.1 Lollipop