前言

Zygote可以說是Android開發面試很高頻的一道問題,但總有小夥伴在回答這道問題總不能讓面試滿意, 在這你就要搞清楚面試問你對Zygote的理解時,面試官最想聽到的和其實想問的應該是哪些?下面我們通過以下幾點來剖析這道問題!

  • 瞭解Zygote的作用
  • 熟悉Zygote的啟動流程
  • 深刻理解Zygote的工作原理

下面來我們來深入剖析

一、 Zygote的作用

Zygote的作用分為兩點:

  • 啟動SystemServer
  • 孵化應用程序

關於這個問題答出了這兩點那就是OK了。可能大部分小夥伴可能能答出第二點,第一點就不是很清楚。SystemServer也是Zygote啟動的,因為SystemServer需要用到Zygote準備好的系統資源包括:

直接從Zygote繼承過來就不需要重新載入過來,那麼對效能將會有很大的提升。

二、Zygote的啟動流程

2.1 啟動三段式

在說Zygote啟動流程之前,先明確一個概念:啟動三段式,這個可以理解為Android中程序啟動的常用套路,分為三步驟:

這裡要了解LOOP迴圈是什麼,其實LOOP作用是不停的接受訊息處理訊息,訊息的來源可以是SoketMessageQueueBinder驅動發過來的訊息,但無論訊息從哪裡來,它整個流程都是去接受訊息,處理訊息。這個啟動三段式,它不光是Zygote程序是這樣的,只要是有獨立程序的,比如說系統服務程序,自己的應用程序都是如此。

2.2 Zygote程序是怎麼啟動的?

Zygote程序的啟動取決於init程序,init程序是它是linux啟動之後使用者空間的第一個程序,下面看一下啟動流程

  1. linux啟動init程序
  2. init程序啟動之後載入init.rc配置檔案

  1. 啟動配置檔案中定義的系統服務,其中Zygote服務就是定義在配置中的

  1. 同時啟動的服務除了Zygote之外還有一些別的系統服務也是會啟動的,比如說ServiceManager程序,它是通過fork+execve系統呼叫啟動的

2.2.1載入Zygote的啟動配置

在init.rc 檔案中會import /init.${ro.zygote}.rc,init.zygoteXX,XX指的是32或者64,對我們沒差我們直接看init.zygote32.rc即可。配置檔案比較長,這裡做了擷取保留了Zygot相關的部分。

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
writepid /dev/cpuset/foreground/tasks
  • service zygote:是程序名稱,
  • /system/bin/app_process:可執行程式的路徑,用於init程序fork,execve呼叫
  • -Xzygote /system/bin --zygote --start-system-server 為它的引數

2.2.2啟動程序

說完了啟動配置呢,這裡來聊一下啟動程序,啟動程序有兩種方式:

第一種:fork+handle

pid_t pid = fork();
if (pid == 0){
// child process
} else {
// parent process
}

第二種:fork+execve

pid_t pid = fork();
if (pid == 0) {
// child process
execve(path, argv, env);
} else {
// parent process
}

兩者看起來差不多,首先首先都會呼叫fork函式建立子程序,這個函式比較奇特會返回兩次,子程序返回一次,父程序返回一次。區別在於:

  • 子程序一次,返回的pid是0 但是父程序返回的pid是子程序的pid,因此可以根據判斷pid來區分目前是子程序還是父程序
  • 對於handle預設的情況,子程序會繼承父程序的所有資源,但當通過execve去載入二進位制程式時,那父程序的資源則會被清除

2.2.3訊號處理-SIGCHLD

當父程序fork子程序後,父程序需要關注這個訊號。當子程序掛了,父程序就會收到SIGCHLD,這時候父程序就可以做一些處理。例如Zygote程序如果掛了,那父程序init程序就會收到訊號將Zygote程序重啟。

三、Zygote程序啟動原理

主要分為兩部分Native層處理和Java層處理,Zygote程序啟動之後,它執行了execve系統呼叫,它執行的是用C++寫的二進位制的可執行程式裡的main函式作為入口,然後在Java層執行!

先來看一下Native層的處理流程

在app_main.cpp檔案,AndroidRuntime.cpp檔案。我們可以找到幾個主要函式名

int main(int argc,char *argv[]){
JavaVM *jvm;
JNIEnv *env;
JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args); //建立Java虛擬機器
jclass clazz = env->FindClass("ZygoteInit"); //找到叫ZygoteInit的Java類
jmethodID method = env->GetStaticMethodID(clazz,"Main","[Ljava/lang/String;)V"); //找到ZygoteInit類中的Main的靜態函式
env->CallStaticVoidMethod(clazz,method,args); //呼叫main函式
jvm->DestroyJavaVM();
}

根據上述程式碼,你會發現在我們的應用裡直接就可以 JNI 呼叫了,並不需要建立虛擬機器。因為應用程序是Zygote程序孵化出來的,繼承了父程序的擁有虛擬機器,只需要重置資料即可。

接著看一下Java層的處理,具體可參考ZygoteInit檔案的main方法

  1. 預載入資源,比如常用類庫、主題資源及一些共享庫等

  1. 啟動SystemServer程序

  1. 進入Socket 的Loop迴圈 會看到的ZygoteServer.runSelectLoop(…)呼叫
boolean runOnce() {
String[] args = readArgumentList(); //讀取引數列表
int pid = Zygote.forkAndSpecialize(); //根據讀取到的引數啟動子程序
if(pid == 0) {
//in child
//執行ActivityThread的入口函式(main)
handleChildProc(args,...);
return true;
}
}

四、總結

Zygote啟動流程中需要主要以下2點問題

  1. Zygote fork要保證是單執行緒
  2. Zygote的IPC是採用socket

看完三件事️



如果你覺得這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:

  1. 點贊,轉發,有你們的 『點贊和評論』,才是我創造的動力。
  2. 關注公眾號 『 阿風的架構筆記 』,不定期分享原創知識。
  3. 同時可以期待後續文章ing
  4. 關注後回覆【666】掃碼即可獲取架構進階學習資料包