Android Java Zygote啟動
最近天氣急劇降溫,大家注意多穿點衣服保護好自己。
年底了,最近實在是太忙了,2020年就剩最後一個月了,哦不,還剩最後半個月了。小憩在這裡再次感謝大家的不離不棄。
雖然更新幅度不大,但小憩儘量保證每篇文章都是儘自己最大的努力去完成,讓大家能夠更容易的看懂與理解,也希望大家能夠有所收穫。
原創不易,不忘初心,繼續前行!
在之前的文章中我們已經分析到 Zygote
最後在 Linux
的 init
程序中是通過如下程式碼啟動 Java
層的 ZygoteInit
。
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
所以我們進入 runtime.start()
方法看下它的具體實現。
AndroidRuntime
frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ALOGD(">>>>>> START %s uid %d <<<<<<\n", className != NULL ? className : "(unknown)", getuid()); static const String8 startSystemServer("start-system-server"); // Whether this is the primary zygote, meaning the zygote which will fork system server. bool primary_zygote = false; /* * '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) { primary_zygote = true; /* 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 /system does not exist."); return; } setenv("ANDROID_ROOT", rootDir, 1); } const char* artRootDir = getenv("ANDROID_ART_ROOT"); if (artRootDir == NULL) { LOG_FATAL("No ART directory specified with ANDROID_ART_ROOT environment variable."); return; } const char* i18nRootDir = getenv("ANDROID_I18N_ROOT"); if (i18nRootDir == NULL) { LOG_FATAL("No runtime directory specified with ANDROID_I18N_ROOT environment variable."); return; } const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT"); if (tzdataRootDir == NULL) { LOG_FATAL("No tz data directory specified with ANDROID_TZDATA_ROOT environment variable."); return; } //const char* kernelHack = getenv("LD_ASSUME_KERNEL"); //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack); /* start the virtual machine */ JniInvocation jni_invocation; jni_invocation.Init(NULL); JNIEnv* env; // 1. 建立虛擬機器 if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { return; } onVmCreated(env); /* * Register android functions. */ // 2. 使用JNI註冊對應的Android Native 方法 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; // 3. 拼接引數,獲取ZygoteInit Class 與 main方法id // 獲取 str = new String[options.size() + 1] stringClass = env->FindClass("java/lang/String"); assert(stringClass != NULL); strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL); assert(strArray != NULL); // 獲取 str[0] = "com.android.internal.os.ZygoteInit" classNameStr = env->NewStringUTF(className); assert(classNameStr != NULL); env->SetObjectArrayElement(strArray, 0, classNameStr); // 獲取 str[1] = "start-system-server" // 獲取 str[2] = "--abi-list=xxxx" 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. */ // 獲取 "com/android/internal/os/ZygoteInit" char* slashClassName = toSlashClassName(className != NULL ? className : ""); // 獲取ZygoteInit Class jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); /* keep going */ } else { // 獲取 ZygoteInit中的main方法對應的方法id 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 { // 4. 呼叫 ZygoteInit.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"); }
根據上面的程式碼註釋,小憩將 start()
方法分為四步:
-
建立虛擬機器
-
通過
JNI
註冊Android Native
方法 -
ZygoteInit Class main id
-
呼叫
ZygoteInit.main()
方法,進去Java
層
建立虛擬機器
通過 startVm()
來建立虛擬機器。
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote) { JavaVMInitArgs initArgs; char propBuf[PROPERTY_VALUE_MAX]; char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX]; char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX]; char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX]; char heapgrowthlimitOptsBuf[sizeof("-XX:HeapGrowthLimit=")-1 + PROPERTY_VALUE_MAX]; char heapminfreeOptsBuf .... parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m"); parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m"); parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit="); parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree="); parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree="); .... 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; }
該方法很長,但主要任務就是設定虛擬機器的相關引數,具體各個屬性的作用就不做分析,感興趣的可以自行 google
對應搜尋。
JNI方法註冊
通過 startReg()
來註冊 Android Native
方法。
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); // 通過JNI註冊 if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { env->PopLocalFrame(NULL); return -1; } env->PopLocalFrame(NULL); //createJavaThread("fubar", quickTest, (void*) "hello"); return 0; }
主要部分是通過 register_jni_procs
來註冊 JNI
方法。對應的是 gRegJNI
static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_os_RuntimeInit), REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit), REG_JNI(register_android_os_SystemClock), REG_JNI(register_android_util_EventLog), REG_JNI(register_android_util_Log), REG_JNI(register_android_util_MemoryIntArray), ... }
方法很多就不一一列舉出來。
舉個例子,例如:
register_com_android_internal_os_ZygoteInit_nativeZygoteInit int register_com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env) { const JNINativeMethod methods[] = { { "nativeZygoteInit", "()V", (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit }, }; return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit", methods, NELEM(methods)); }
對應的就是 com/android/internal/os/ZygoteInit
中的 nativeZygoteInit()
方法,而 nativeZygoteInit()
方法通過 JNI
註冊之後,在 Linux
的具體實現是對應的 com_android_internal_os_ZygoteInit_nativeZygoteInit()
方法。
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz) { gCurRuntime->onZygoteInit(); }
最後它會呼叫 onZygoteInit()
方法,它的具體實現在 app_main.cpp
中
virtual void onZygoteInit() { sp<ProcessState> proc = ProcessState::self(); ALOGV("App process: starting thread pool.\n"); proc->startThreadPool(); }
引數、類、方法構建與呼叫
在這一步主要通過 FindClass()
方法來獲取對應 Java
的 Class
型別,構建對應的 String[]
型別的引數與 ZygoteInit Class
。
再通過 GetStaticMethodID()
方法來獲取對應 main()
方法的方法 id
,以便為之後呼叫 ZygoteInit.main()
方法做準備。
最後在通過 CallStaticVoidMethod()
來呼叫 ZygoteInit.main()
方法,最終進入 Java
層的 ZygoteInit
。
從 runtime.start()
到呼叫 Java
層的 ZygoteInit.main()
的整個過程流程圖如下:
ZygoteInit
public static void main(String argv[]) { // 建立zygoteServer ZygoteServer zygoteServer = new ZygoteServer(); ZygoteHooks.startZygoteNoThreadCreation(); // 設定zygote自己的程序group id try { Os.setpgid(0, 0); } catch (ErrnoException ex) { throw new RuntimeException("Failed to setpgid(0,0)", ex); } final Runnable caller; try { // Report Zygote start time to tron unless it is a runtime restart if (!"1".equals(SystemProperties.get("sys.boot_completed"))) { MetricsLogger.histogram(null, "boot_zygote_init", (int) SystemClock.elapsedRealtime()); } String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing"; TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag, Trace.TRACE_TAG_DALVIK); bootTimingsTraceLog.traceBegin("ZygoteInit"); RuntimeInit.enableDdms(); // 解析引數,這些引數來自於`Linux`層 boolean startSystemServer = false; String socketName = "zygote"; String abiList = null; boolean enableLazyPreload = false; for (int i = 1; i < argv.length; i++) { // 是否需要啟動system_server服務 if ("start-system-server".equals(argv[i])) { startSystemServer = true; } else if ("--enable-lazy-preload".equals(argv[i])) { // 是否需要懶載入 enableLazyPreload = true; } else if (argv[i].startsWith(ABI_LIST_ARG)) { // 獲取abi_list abiList = argv[i].substring(ABI_LIST_ARG.length()); } else if (argv[i].startsWith(SOCKET_NAME_ARG)) { // 獲取socket名稱 socketName = argv[i].substring(SOCKET_NAME_ARG.length()); } else { throw new RuntimeException("Unknown command line argument: " + argv[i]); } } if (abiList == null) { throw new RuntimeException("No ABI list supplied."); } // 註冊zygote的socket zygoteServer.registerServerSocketFromEnv(socketName); // In some configurations, we avoid preloading resources and classes eagerly. // In such cases, we will preload things prior to our first fork. if (!enableLazyPreload) { bootTimingsTraceLog.traceBegin("ZygotePreload"); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); // 預載入 preload(bootTimingsTraceLog); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); bootTimingsTraceLog.traceEnd(); // ZygotePreload } else { Zygote.resetNicePriority(); } // Do an initial gc to clean up after startup bootTimingsTraceLog.traceBegin("PostZygoteInitGC"); // 觸發gc gcAndFinalize(); bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC bootTimingsTraceLog.traceEnd(); // ZygoteInit // Disable tracing so that forked processes do not inherit stale tracing tags from // Zygote. Trace.setTracingEnabled(false, 0); Zygote.nativeSecurityInit(); // Zygote process unmounts root storage spaces. Zygote.nativeUnmountStorageOnInit(); ZygoteHooks.stopZygoteNoThreadCreation(); // 啟動 system_server if (startSystemServer) { Runnable r = forkSystemServer(abiList, socketName, zygoteServer); // {@code r == null} in the parent (zygote) process, and {@code r != null} in the // child (system_server) process. if (r != null) { r.run(); return; } } Log.i(TAG, "Accepting command socket connections"); // 進入迴圈模式 caller = zygoteServer.runSelectLoop(abiList); } catch (Throwable ex) { Log.e(TAG, "System zygote died with exception", ex); throw ex; } finally { zygoteServer.closeServerSocket(); } // We're in the child process and have exited the select loop. Proceed to execute the // command. if (caller != null) { caller.run(); } }
終於見到 Java
程式碼了,不容易啊~
首先會建立 zygoteServer
,為 Zygote
設定自己的程序分組 id
;然後會解析傳遞過來的引數,根據引數執行之後的後續操作。
註冊Socket
void registerServerSocketFromEnv(String socketName) { if (mServerSocket == null) { int fileDesc; final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; try { String env = System.getenv(fullSocketName); fileDesc = Integer.parseInt(env); } catch (RuntimeException ex) { throw new RuntimeException(fullSocketName + " unset or invalid", ex); } try { FileDescriptor fd = new FileDescriptor(); fd.setInt$(fileDesc); mServerSocket = new LocalServerSocket(fd); mCloseSocketFd = true; } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); } } }
通過 registerServerSocketFromEnv
來註冊 socket
,使用 LocalServerSocket
建立本地 socket
服務,來監聽對於的檔案描述符 fd
。
預載入
static void preload(TimingsTraceLog bootTimingsTraceLog) { // 預載入位於/system/etc/preloaded-classes檔案中的類 preloadClasses(); // 預載入資源,儲存drawable與color preloadResources(); // 預載入OpenGL preloadOpenGL(); // 預載入共享庫,包括android、compiler_rt、jnigraphics preloadSharedLibraries(); // 預載入 文字連線符資源 preloadTextResources(); // 僅用於zygote程序,用於記憶體共享的程序 WebViewFactory.prepareWebViewInZygote(); endIcuCachePinning(); warmUpJcaProviders(); sPreloadComplete = true; }
對於類載入,採用反射機制 Class.forName()
方法來載入。
對於資源載入,主要是 com.android.internal.R.array.preloaded_drawables
和 com.android.internal.R.array.preloaded_color_state_lists
,在應用程式中以 com.android.internal.R.xxx
開頭的資源,便是此時由 Zygote
載入到記憶體的。
在這裡預載入目的是為了之後 fork
出子的程序,同時使用 copy on write
技術,使得子程序在只讀模式下與父程序共用一塊記憶體空間,從而保證子程序能夠迅速 fork
處理,減少資料的拷貝數量。
SystemServer
if (startSystemServer) { Runnable r = forkSystemServer(abiList, socketName, zygoteServer); // {@code r == null} in the parent (zygote) process, and {@code r != null} in the // child (system_server) process. if (r != null) { r.run(); return; } }
建立 system_server
服務,具體實現後續到 system_server
的時候再分析。
runSelectLoop
Runnable runSelectLoop(String abiList) { ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>(); ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>(); // 儲存fd fds.add(mServerSocket.getFileDescriptor()); peers.add(null); while (true) { StructPollfd[] pollFds = new StructPollfd[fds.size()]; for (int i = 0; i < pollFds.length; ++i) { pollFds[i] = new StructPollfd(); pollFds[i].fd = fds.get(i); pollFds[i].events = (short) POLLIN; } try { // 輪詢,當pollFds有事件到來則往下執行,否則阻塞在這裡 Os.poll(pollFds, -1); } catch (ErrnoException ex) { throw new RuntimeException("poll failed", ex); } for (int i = pollFds.length - 1; i >= 0; --i) { // 採用I/O多路複用機制,當接收到客戶端發出連線請求 或者資料處理請求到來,則往下執行 // 否則進入continue,跳出本次迴圈 if ((pollFds[i].revents & POLLIN) == 0) { continue; } if (i == 0) { // 即fds[0],代表的是sServerSocket,則意味著有客戶端連線請求 // 則建立ZygoteConnection物件,並新增到fds ZygoteConnection newPeer = acceptCommandPeer(abiList); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); } else { // i>0,則代表通過socket接收來自對端的資料,並執行相應操作 try { // 獲取連結 ZygoteConnection connection = peers.get(i); // 執行操作 final Runnable command = connection.processOneCommand(this); if (mIsForkChild) { if (command == null) { throw new IllegalStateException("command == null"); } return command; } else { if (command != null) { throw new IllegalStateException("command != null"); } if (connection.isClosedByPeer()) { connection.closeSocket(); peers.remove(i); fds.remove(i); } } } catch (Exception e) { if (!mIsForkChild) { Slog.e(TAG, "Exception executing zygote command: ", e); ZygoteConnection conn = peers.remove(i); conn.closeSocket(); fds.remove(i); } else { Log.e(TAG, "Caught post-fork exception in child process.", e); throw e; } } finally { mIsForkChild = false; } } } } }
Zygote
採用高效的 I/O
多路複用機制,保證在沒有客戶端連線請求或資料處理時休眠,否則響應客戶端的請求。
processOneCommand
Runnable processOneCommand(ZygoteServer zygoteServer) { String args[]; Arguments parsedArgs = null; FileDescriptor[] descriptors; try { // 讀取引數 args = readArgumentList(); descriptors = mSocket.getAncillaryFileDescriptors(); } catch (IOException ex) { throw new IllegalStateException("IOException on command socket", ex); } // readArgumentList returns null only when it has reached EOF with no available // data to read. This will only happen when the remote socket has disconnected. if (args == null) { isEof = true; return null; } int pid = -1; FileDescriptor childPipeFd = null; FileDescriptor serverPipeFd = null; // 解析引數,並進行轉換 parsedArgs = new Arguments(args); ... fd = zygoteServer.getServerSocketFileDescriptor(); if (fd != null) { fdsToClose[1] = fd.getInt$(); } fd = null; pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote, parsedArgs.instructionSet, parsedArgs.appDataDir); try { if (pid == 0) { // 子程序執行 zygoteServer.setForkChild(); zygoteServer.closeServerSocket(); IoUtils.closeQuietly(serverPipeFd); serverPipeFd = null; return handleChildProc(parsedArgs, descriptors, childPipeFd, parsedArgs.startChildZygote); } else { // 父程序執行 IoUtils.closeQuietly(childPipeFd); childPipeFd = null; handleParentProc(pid, descriptors, serverPipeFd); return null; } } finally { IoUtils.closeQuietly(childPipeFd); IoUtils.closeQuietly(serverPipeFd); } }
通過 fork
方法來建立子程序,該方法會返回兩次結果;如果為 0
則代表當前需要執行子程序的相關邏輯,非 0
則是父程序的邏輯。
至此 Zygote
的啟動就完成了。
所以在 Java
層 Zygote
主要做的事情為:
-
通過
registerServerSocketFromEnv
來註冊socket
-
preload drawable color openGL WebView
-
建立
system_server
服務 -
runSelectLoop
等待新訊息的到來,並建立新程序
Zygote
中的一個重要步驟:啟動 system_server
後續再進行分析,敬請期待!
推薦閱讀
- 協程到底是怎麼切換執行緒的?
- 元件化開花,就問你香不香
- Kotlin 基礎 | 拒絕語法噪音
- 玩轉LayoutInflater
- 淺談Android熱更新的前因後果
- 如何學好設計,做好架構? 核心思想才是關鍵
- Kotlin 內聯類 inline class請了解一下
- Android MaterialButton使用詳解,告別shape、selector
- 入木三分:從設計者角度看Retrofit原理
- 使用Jetpack Compose完成你的自定義Layout
- 為了能夠摸魚,我走上了歧路
- 自信,這是最好的ThreadLocal分析
- Android&Kotlin編譯速度原理剖析
- Android 面試之必問Java基礎
- Kotlin 基礎 | 委託及其應用
- 引入Jetpack架構後,你的App會發生哪些變化?
- Handler那些事
- android佈局優化的幾個建議
- 面試:ViewModel為何橫豎屏切換時不銷燬?
- Android Java Zygote啟動