大廠Android崗高頻面試問題:說說你對Zygote的理解!

前言

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

  • 瞭解Zygote的做用
  • 熟悉Zygote的啓動流程
  • 深入理解Zygote的工做原理

下面來咱們來深刻剖析linux

1、 Zygote的做用

Zygote的做用分爲兩點:android

  • 啓動SystemServer
  • 孵化應用進程

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

img

直接從Zygote繼承過來就不須要從新加載過來,那麼對性能將會有很大的提高。面試

2、Zygote的啓動流程

2.1 啓動三段式

在說Zygote啓動流程以前,先明確一個概念:啓動三段式,這個能夠理解爲Android中進程啓動的經常使用套路,分爲三步驟:架構

img

這裏要了解LOOP循環是什麼,其實LOOP做用是不停的接受消息處理消息,消息的來源能夠是SoketMessageQueueBinder驅動發過來的消息,但不管消息從哪裏來,它整個流程都是去接受消息,處理消息。這個啓動三段式,它不光是Zygote進程是這樣的,只要是有獨立進程的,好比說系統服務進程,本身的應用進程都是如此。app

2.2 Zygote進程是怎麼啓動的?

Zygote進程的啓動取決於init進程,init進程是它是linux啓動以後用戶空間的第一個進程,下面看一下啓動流程jvm

  1. linux啓動init進程
  2. init進程啓動以後加載init.rc配置文件

img

  1. 啓動配置文件中定義的系統服務,其中Zygote服務就是定義在配置中的

img

  1. 同時啓動的服務除了Zygote以外還有一些別的系統服務也是會啓動的,好比說ServiceManager進程,它是經過fork+execve系統調用啓動的

img

2.2.1加載Zygote的啓動配置

在init.rc 文件中會import /init.${ro.zygote}.rc,init.zygoteXX,XX指的是32或者64,對咱們沒差咱們直接看init.zygote32.rc便可。配置文件比較長,這裏作了截取保留了Zygot相關的部分。socket

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進程重啓。

img

3、Zygote進程啓動原理

主要分爲兩部分Native層處理和Java層處理,Zygote進程啓動以後,它執行了execve系統調用,它執行的是用C++寫的二進制的可執行程序裏的main函數做爲入口,而後在Java層運行!

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

img

在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. 預加載資源,好比經常使用類庫、主題資源及一些共享庫等

img

  1. 啓動SystemServer進程

img

  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;
	}
}

img

4、總結

Zygote啓動流程中須要主要如下2點問題

  1. Zygote fork要保證是單線程
  2. Zygote的IPC是採用socket

看完三件事❤️


若是你以爲這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:

  1. 點贊,轉發,有大家的 『點贊和評論』,纔是我創造的動力。
  2. 關注公衆號 『 阿風的架構筆記 』,不按期分享原創知識。
  3. 同時能夠期待後續文章ing🚀
  4. 關注後回覆【666】掃碼便可獲取架構進階學習資料包
相關文章
相關標籤/搜索