跟我學:Android面試之深入Android Framework談談對Zygot的理解


跟我學:Android面試之深入Android Framework談談對Zygot的理解


導讀

本人從事Android客戶端開發,最近在在深入Framwork學習,希望我的學習分享能幫到你。首先我們看一下關於Zygote我們應該如何去分析,這裡通過一張思維導圖幫助大家整理思路。思維導圖一方面羅列了本文的大綱,另一方面也是希望讀者朋友可以保存圖片至自己的複習資料裡。以便隨時通過該大綱去複習,可以關注我,收藏文章進一步溝通學習!


跟我學:Android面試之深入Android Framework談談對Zygot的理解


談談對Zygote的理解

當遇到這樣一道面試題,我們應該分析面試官想考察的是什麼?

  • 瞭解Zygote的作用,初級的要求,答出來後方能深入
  • 熟悉Zygote的啟動流程,中級要求,主要是啟動中做的事有哪些關鍵步驟
  • 深刻理解Zygote的工作原理,高級要求,主要是怎麼啟動進程的,怎麼與其他進程通訊

Zygote的作用

他的作用非常簡單就兩點

  • 啟動SystemServer
  • 孵化應用進程

如果答出了這兩點那就是及格了。但可能大部分朋友只知道第二點,第一點就不是那麼清楚。其實SystemServer也是Zygote啟動的,因為SystemServer需要用到Zygote準備好的系統資源:包括我們常用的一些類、註冊的JNI函數、主題資源及一些共享庫等等,直接從Zygote繼承過來自己就不需要重新加載過來,那麼對性能將會有很大的提升。

Zygote的啟動流程

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

  1. 進程啟動
  2. 準備工作
  3. LOOP循環

其中LOOP作用是不停的接受消息,處理消息,消息的來源可以是Soket、MessageQueue、Binder驅動發過來的消息,但無論消息從哪裡來,它總的流程都是去接受消息,處理消息。

這個啟動三段式,他不光是Zygote進程是這樣的,只要是有獨立進程的,比如說系統服務進程,自己的應用進程都是這樣的。


下面我們來看一下Zygote進程是怎麼啟動的?

Zygote進程的啟動,得感謝init進程。init進程是它是linux啟動之後用戶空間的第一個進程。下面看一下啟動流程

  1. linux啟動init進程
  2. 加載init.rc配置文件
  3. 啟動配置文件中定義的系統服務,其中Zygote服務就是定義在配置中的
  4. init進程通過fork + execve 系統調用啟動Zygote

在分析具體系統源碼實現之前,我分享一個看源碼很方便網站Android社區:www.androidos.net.cn

下面列舉了本文需要閱讀的源碼文件路徑,供大家方便查找。

<code>platform/system/core/rootdir/init.zygoteXX.rc
platform/system/core/rootdir/init.rc
platform/frameworks/base/cmds/app_process/app_main.cpp
platform/frameworks/base/core/jni/AndroidRuntime.cpp
platform/libnativehelper/JniInvocation.cpp
platform/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java/<code>

加載Zygote的啟動配置

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

<code>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/<code>
  • service zygote:是進程名稱,
  • /system/bin/app_process:可執行程序的路徑,用於init進程fork,execve調用
  • -Xzygote /system/bin --zygote --start-system-server 為它的參數

如何啟動,啟動方式有兩種

  • fork + handle
  • fork + execve

首先都會調用fork創建新的進程,比較奇特的是它會返回兩次。

  • 子進程一次,返回的pid是0 父進程一次,返回的pid是子進程的pid
  • 對於handle默認的情況,子進程會繼承父進程的所有資源,但當通過execve去加載二進制程序時,那父進程的資源則會被清除

還有個一個關注點是,信號處理-SIGCHLD

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


Zygote進程啟動後做了什麼

主要分為兩部分Native層處理和Java層處理。

先來看一下Native層的處理

  • 啟動Android虛擬機
  • 註冊Android的JNI函數
  • 進入Java層

在app_main.cpp文件,AndroidRuntime.cpp文件。我們可以找到幾個主要函數名

<code>JNI_CreateJavaVM   // 創建啟動虛擬機
jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit",
methods, NELEM(methods)) // 通過反射獲取ZygoteInit對象
env->CallStaticVoidMethod(startClass, startMeth, strArray);//調用main函數,進入Java層/<code>

在應用進程中並不需要創建虛擬機,因為應用進程是Zygote進程孵化出來的,繼承了父進程的擁有虛擬機,只需要重置數據即可。

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

  • 預加載資源,常用類庫、主題資源及一些共享庫等
  • 啟動SystemServer進程
  • 進入Socket 的Loop循環 會看到的ZygoteServer.runSelectLoop(...)調用

Zygote的工作原理

Zygote的LOOP循環會一直監聽Socket中的內容,所以Zygote與其他進程的通信是通過Socket的,工作原理舉個例子

  1. 桌面應用點擊某應用圖標,若應用沒有啟動進程
  2. AMS會通過Socket通知Zygote進程
  3. Zygote進程接受到消息後fork出一個應用進程
  4. 執行應用進程的ActivityThread的main方法

要注意的細節

Zygote fork要單線程進行,在fork時Zygote會將除主進程外的所有線程都停了成功後重啟,因為對於新創建的子進程而言只有主線程,避免多線程的死鎖問題

Zygote的IPC沒有采用binder,而是本地Socket

最後

看到最後,我相信你一定對Zygote有一定了解了吧。回頭看看第一部分的考查內容講講唄,對於每一個點的重點我都細心的為你做了高亮。還有紙上得來終覺淺,絕知此事要躬行。本文參考了Android api28的源碼,希望你也去踩著點過一遍源碼,肯定能加深理解!

我是Yangcy,該吃吃該喝喝,該學還得學,我們一起加油!

跟我學:Android面試之深入Android Framework談談對Zygot的理解


分享到:


相關文章: