Zygote可以說是Android開發面試很高頻的一道問題,但總有小伙伴在回答這道問題總不能讓面試滿意, 在這你就要搞清楚面試問你對Zygote的理解時,他最想聽到的和其實想問的應該是哪些?下面我們通過以下幾點來剖析這道問題!
- 了解Zygote的作用
- 熟悉Zygote的啟動流程
- 深刻理解Zygote的工作原理
下面來我們來深入剖析
一、 Zygote的作用
Zygote的作用分為兩點:
- 啟動SystemServer
- 孵化應用進程
關于這個問題答出了這兩點那就是OK了??赡艽蟛糠中』锇榭赡苣艽鸪龅诙c,第一點就不是很清楚。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配置文件
3.啟動配置文件中定義的系統服務,其中Zygote服務就是定義在配置中的
4.同時啟動的服務除了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.預加載資源,比如常用類庫、主題資源及一些共享庫等
2.啟動SystemServer進程
3.進入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