前言
Zygote可以說是Android開發面試很高頻的一道問題,但總有小伙伴在回答這道問題總不能讓面試滿意, 在這你就要搞清楚面試問你對Zygote的理解時,他最想聽到的和其實想問的應該是哪些?下面我們通過以下幾點來剖析這道問題!
- 了解Zygote的作用
- 熟悉Zygote的啟動流程
- 深刻理解Zygote的作業原理
下面來我們來深入剖析
一、 Zygote的作用
Zygote的作用分為兩點:
- 啟動SystemServer
- 范訓應用行程
關于這個問題答出了這兩點那就是OK了,可能大部分小伙伴可能能答出第二點,第一點就不是很清楚,SystemServer也是Zygote啟動的,因為SystemServer需要用到Zygote準備好的系統資源包括:

直接從Zygote繼承過來就不需要重新加載過來,那么對性能將會有很大的提升,
二、Zygote的啟動流程
2.1 啟動三段式
在說Zygote啟動流程之前,**先明確一個概念:啟動三段式,**這個可以理解為Android中行程啟動的常用套路,分為三步驟:

這里要了解LOOP回圈是什么,其實LOOP作用是不停的接受訊息,處理訊息,訊息的來源可以是Soket、MessageQueue、Binder驅動發過來的訊息,但無論訊息從哪里來,它整個流程都是去接受訊息,處理訊息,這個啟動三段式,它不光是Zygote行程是這樣的,只要是有獨立行程的,比如說系統服務行程,自己的應用行程都是如此,
2.2 Zygote行程是怎么啟動的?
Zygote行程的啟動取決于init行程,init行程是它是linux啟動之后用戶空間的第一個行程,下面看一下啟動流程:
- linux啟動init行程
- init行程啟動之后加載init.rc組態檔

- 啟動組態檔中定義的系統服務,其中Zygote服務就是定義在配置中的

- 同時啟動的服務除了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方法
- 預加載資源,比如常用類別庫、主題資源及一些共享庫等

- 啟動SystemServer行程

- 進入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點問題
- Zygote fork要保證是單執行緒
- Zygote的IPC是采用socket
有疑問或者討論可以在評論區交流,同時不要忘了一鍵三連啦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/295693.html
標籤:其他
