從源碼的角度看安卓開機流程-ZygoteInit

從源碼的角度看安卓開機流程-ZygoteInit

書接上文

上一篇文章分享了app_main的主流程,最後會調用AndroidRuntime的start()方法執行ZygoteInit。這裡從native code運行Java code。


本文涉及到zygote進程和system_server進程。

ZygoteInit簡介

從源碼的角度看安卓開機流程-ZygoteInit

ZygoteInit.java經過編譯最終生成到framework.jar,生成到系統的/system/framework/目錄。系統啟動的時候,會進行加載。

這個類是zygote進程的啟動類。預初始化一些類,然後會等待sockets傳來的命令。根據傳來的命令,創建子進程,子進程會繼承虛擬機的初始狀態。

源碼分析

源碼在下面的路徑,一共1067行。我們直接看ZygoteInit的main()方法。

Frameworks/base/core/java/com/android/internal/os/ZygoreInit.java

從源碼的角度看安卓開機流程-ZygoteInit

根據傳入的參數設置局部變量的值。startSystemServer=true、abiList=armeabi-v7a,armeabi、socketName使用默認值zygote。

從源碼的角度看安卓開機流程-ZygoteInit

從源碼的角度看安卓開機流程-ZygoteInit

創建socket,並啟動listen模式。這裡還有一個細節比較模糊:fd的由來是怎樣一個邏輯?涉及到init進程一些細節,後面再詳細分析。

從源碼的角度看安卓開機流程-ZygoteInit

preload()方法進行預加載,依次包括ICU data、java class文件、resources、openGL、sharedLibraries、TextResources。

從源碼的角度看安卓開機流程-ZygoteInit

從源碼的角度看安卓開機流程-ZygoteInit

這裡是啟動子進程system_server。system_server進程,在下面的章節分析。這裡只看父進程zgote的邏輯。在父進程zygote這裡調用fork()返回的pid是大於零的整數,所以這個函數會直接返回true,進行下面的流程。

從源碼的角度看安卓開機流程-ZygoteInit

從源碼的角度看安卓開機流程-ZygoteInit

這裡會進入無限循環,等待socket IPC,執行應用程序創建命令。其它應用程序就是通過這裡fork()子進程,創建出來的。應用程序的啟動流程後面會專門分析,此處不做展開。

至此,zygote進程的啟動流程就分析完了。它是通過native的可執行程序app_process調用到Java層的ZygoteInit,完成進程創建。可執行程序app_process是配置在init.zygote.rc文件裡面,由init進程解析執行。

system_server進程啟動

system_server的啟動涉及到多線程編程的知識,主要是fork()和waitpid()兩個函數的使用,不瞭解這塊知識的請先參看備註1。

system_server是zygote進程的子進程。上文分析了父進程zygote的啟動流程。這裡分析子進程system_server的啟動流程。

瞭解了fork()的知識點後,我們知道system_server和zygote兩個進程在前面的code是沒有差異的,差異是在調用fork()函數之後,代碼的執行邏輯才有了差異。

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 的main()方法會調用startSystemServer()方法

從源碼的角度看安卓開機流程-ZygoteInit

直接看關鍵部分,Zygote.forkSystemServer()返回的pid是0,然後會調用handleSystemServerProcess()。這裡的關鍵邏輯就是調用fork()方法,從zygote分裂出system_server進程。

從源碼的角度看安卓開機流程-ZygoteInit

a:system_server進程不需要socket進行IPC,關閉zygote創建的socket。

b:設置進程名字。niceName是上一步傳入的"system_server"。

c:從環境變量讀取systemserver jar包的路徑,並進行加載。通過命令env可以查看系統的環境變量。

從源碼的角度看安卓開機流程-ZygoteInit

d:把剩餘的參數傳給SystemServer類

從源碼的角度看安卓開機流程-ZygoteInit

a:進行一些簡單的初始化設置。時區設置,日誌復位,代理設置,網絡設置等等。

b:這是一個native的方法,暫時不分析。

c:字面意思,應用程序初始化。

從源碼的角度看安卓開機流程-ZygoteInit

這個方法進行一些虛擬機設置,參數解析,然後調用invokeStaticMain()。

從源碼的角度看安卓開機流程-ZygoteInit

通過Java的反射機制取得SystemServer類的main()方法,然後主動拋出一個預設的異常,傳入main()方法和main()方法的參數。

從源碼的角度看安卓開機流程-ZygoteInit

現在回到zygoteInit.main(),捕獲MethodAndArgsCaller異常,然後調用run()方法。這是一個很巧妙的設計,達到執行目標方法的同時,可以繞過zygote的loop邏輯,也可以擺脫zygote的堆棧的束縛。

從源碼的角度看安卓開機流程-ZygoteInit

這裡已經很明顯了,Java反射機制,執行SystemServer.main(),開始進入system_server的主邏輯。

下一篇文章分析system_server的主邏輯。

備註:

1 關於fork()和waitpid()

調用fork()函數,會創建一個新的進程,叫做子進程,對應的就是父進程。fork()函數的返回值是進程ID(pid),pid在父進程和子進程的值是不一樣的。在父進程中得到的pid值是子進程的進程ID,是大於零的值;在子進程中,得到的pid的值是0。

父進程通過fork()可以卵化一個子進程,相當於一個進程變成兩個進程,同時具有以下特點:

1. 這兩個進程的代碼一致,且代碼執行到的位置一致。

2. 區別是進程ID(PID)不一樣

3. 一次調用,兩次返回。父進程返回的是子進程的PID,從而父進程可以跟蹤子進程的狀態,以子進程的PID作為函數參數。子進程返回的PID是0。

父進程調用waitpid()函數,傳入子進程的pid作為參數,可以查看子進程的狀態或是進行某種同步。

一個簡單的例子:父進程通過fork()創建一個子進程,然後進入等待狀態,等子進程執行完成,父進程再執行自己的任務。(system_server啟動邏輯中,waitpid()第三個參數傳入的是1,只是檢查子進程的狀態,不會阻塞。)

從源碼的角度看安卓開機流程-ZygoteInit

程序運行結果:

從源碼的角度看安卓開機流程-ZygoteInit


分享到:


相關文章: