導讀
本人從事Android客戶端開發,最近在在深入Framwork學習,希望我的學習分享能幫到你。首先我們看一下關於Zygote我們應該如何去分析,這裡通過一張思維導圖幫助大家整理思路。思維導圖一方面羅列了本文的大綱,另一方面也是希望讀者朋友可以保存圖片至自己的複習資料裡。以便隨時通過該大綱去複習,可以關注我,收藏文章進一步溝通學習!
談談對Zygote的理解
當遇到這樣一道面試題,我們應該分析面試官想考察的是什麼?
- 瞭解Zygote的作用,初級的要求,答出來後方能深入
- 熟悉Zygote的啟動流程,中級要求,主要是啟動中做的事有哪些關鍵步驟
- 深刻理解Zygote的工作原理,高級要求,主要是怎麼啟動進程的,怎麼與其他進程通訊
Zygote的作用
他的作用非常簡單就兩點
- 啟動SystemServer
- 孵化應用進程
如果答出了這兩點那就是及格了。但可能大部分朋友只知道第二點,第一點就不是那麼清楚。其實SystemServer也是Zygote啟動的,因為SystemServer需要用到Zygote準備好的系統資源:包括我們常用的一些類、註冊的JNI函數、主題資源及一些共享庫等等,直接從Zygote繼承過來自己就不需要重新加載過來,那麼對性能將會有很大的提升。
Zygote的啟動流程
在說Zygote啟動流程之前,我們可以明確一個概念:啟動三段式,這個可以理解為Android中進程啟動的常用套路,分為三步驟
- 進程啟動
- 準備工作
- LOOP循環
其中LOOP作用是不停的接受消息,處理消息,消息的來源可以是Soket、MessageQueue、Binder驅動發過來的消息,但無論消息從哪裡來,它總的流程都是去接受消息,處理消息。
這個啟動三段式,他不光是Zygote進程是這樣的,只要是有獨立進程的,比如說系統服務進程,自己的應用進程都是這樣的。
下面我們來看一下Zygote進程是怎麼啟動的?
Zygote進程的啟動,得感謝init進程。init進程是它是linux啟動之後用戶空間的第一個進程。下面看一下啟動流程
- linux啟動init進程
- 加載init.rc配置文件
- 啟動配置文件中定義的系統服務,其中Zygote服務就是定義在配置中的
- 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的,工作原理舉個例子
- 桌面應用點擊某應用圖標,若應用沒有啟動進程
- AMS會通過Socket通知Zygote進程
- Zygote進程接受到消息後fork出一個應用進程
- 執行應用進程的ActivityThread的main方法
要注意的細節
Zygote fork要單線程進行,在fork時Zygote會將除主進程外的所有線程都停了成功後重啟,因為對於新創建的子進程而言只有主線程,避免多線程的死鎖問題
Zygote的IPC沒有采用binder,而是本地Socket
最後
看到最後,我相信你一定對Zygote有一定了解了吧。回頭看看第一部分的考查內容講講唄,對於每一個點的重點我都細心的為你做了高亮。還有紙上得來終覺淺,絕知此事要躬行。本文參考了Android api28的源碼,希望你也去踩著點過一遍源碼,肯定能加深理解!
我是Yangcy,該吃吃該喝喝,該學還得學,我們一起加油!
閱讀更多 Yangcy跟我學 的文章