談?wù)剬ndroid中Zygote的理解

Zygote是什么?

在Android中,負責孵化新進程的這個進程叫做Zygote躺酒,安卓上其他的應(yīng)用進程都是由它孵化的。眾所周知蔑歌,安卓是Linux內(nèi)核羹应,安卓系統(tǒng)上運行的一切程序都是放在Dalvik虛擬機上的,Zygote也不例外次屠,事實上园匹,它是安卓運行的第一個Dalvik虛擬機進程。既然Zygote負責孵化其他的安卓進程劫灶,那么它自己是由誰孵化的呢裸违?既然Android是基于Linux內(nèi)核,那么Zygote當然就是Linux內(nèi)核啟動的用戶級進程Init創(chuàng)建的了本昏。

Zygote的作用是什么供汛?

對于Zygote的作用實際上可以概括為以下兩點:

  • 創(chuàng)建SystemServer
  • 孵化應(yīng)用進程

Zygote的啟動過程

  • Zygote進程在Init進程啟動過程中被以service服務(wù)的形式啟動:

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system 
    
  • 調(diào)用app_main.cpp的main函數(shù)中的AppRuntime的start方法來啟動Zygote進程

  • 調(diào)用startVm函數(shù)來創(chuàng)建虛擬機,調(diào)用startReg函數(shù)為java虛擬機注冊JNI方法

  • 通過toSlashClassName找到ZygoteInit涌穆,通過GetStaticMethedID函數(shù)找到main方法然后調(diào)用怔昨,ZygoteInit的main方法是由Java語言編寫的,當前的運行邏輯在Native中蒲犬,這就需要JNI來調(diào)用Java朱监,這樣Zygote就從Native層進入了Java框架層岸啡。

  • ZygoteInit的main方法的源碼如下:

    frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
    public static void main(String argv[]) {
       //1原叮、創(chuàng)建ZygoteServer
        ZygoteServer zygoteServer = new ZygoteServer();
      .......
        try {
             .......
            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
           // 2、解析app_main.cpp傳來的參數(shù)
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                } else if ("--enable-lazy-preload".equals(argv[i])) {
                    enableLazyPreload = true;
                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[I]);
                }
            }

            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }
            //3、創(chuàng)建一個Server端的Socket
            zygoteServer.registerServerSocket(socketName);
            // In some configurations, we avoid preloading resources and classes eagerly.
            // In such cases, we will preload things prior to our first fork.
            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
               //4奋隶、加載進程的資源和類
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
            }
              ........
            if (startSystemServer) {
                //5擂送、開啟SystemServer進程,這是受精卵進程的第一次分裂
                startSystemServer(abiList, socketName, zygoteServer);
            }

            Log.i(TAG, "Accepting command socket connections");
           //6唯欣、啟動一個死循環(huán)監(jiān)聽來自Client端的消息
            zygoteServer.runSelectLoop(abiList);
           //7嘹吨、關(guān)閉SystemServer的Socket
            zygoteServer.closeServerSocket();
        } catch (Zygote.MethodAndArgsCaller caller) {
           //8、這里捕獲這個異常調(diào)用MethodAndArgsCaller的run方法境氢。
            caller.run();
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            zygoteServer.closeServerSocket();
            throw ex;
        }
    }

總結(jié)下來的流程:

【1】通過registerServerSocket方法來創(chuàng)建一個Server端的socket蟀拷,這個name為zygote的socket用于等待ActivityManagerService請求Zygote來創(chuàng)建新的應(yīng)用程序進程

【2】預(yù)加載,預(yù)加載項如下:

preloadClasses();
preloadResources();
preloadOpenGL();
preloadSharedLibraries();
preloadTextResources();
WebViewFactory.prepareWebViewInZygote();
...

以下是Android應(yīng)用進程共享內(nèi)存圖:

通過上圖可以很容易理解在Zygote進程預(yù)加載系統(tǒng)資源后萍聊,然后通過它孵化出其他的虛擬機進程问芬,進而共享虛擬機內(nèi)存和框架層資源,這樣大幅度提高應(yīng)用程序的啟動和運行速度寿桨。

【3】啟動SystemServer進程

【4】執(zhí)行runSelectLoop()方法等待消息去創(chuàng)建應(yīng)用進程

Zygote注意細節(jié)

  • Zygote進行fork的時候要是單線程此衅,為了避免造成死鎖或者狀態(tài)不一致等問題
  • Zygote的跨進程通信沒有采用Binder機制,而是采用本地socket

關(guān)于Zygote的疑問

  • 孵化應(yīng)用進程這種事為什么不交給SystemServer來做亭螟,而專門設(shè)計一個Zygote挡鞍?

我們知道,應(yīng)用在啟動的時候需要做很多準備工作预烙,包括啟動虛擬機墨微,加載各類系統(tǒng)資源等等,這些都是非常耗時的扁掸,如果能在zygote里就給這些必要的初始化工作做好欢嘿,子進程在fork的時候就能直接共享,那么這樣的話效率就會非常高也糊。這個就是zygote存在的價值炼蹦,這一點呢SystemServer是替代不了的,主要是因為SystemServer里跑了一堆系統(tǒng)服務(wù)狸剃,這些是不能繼承到應(yīng)用進程的掐隐。而且我們應(yīng)用進程在啟動的時候,內(nèi)存空間除了必要的資源外钞馁,最好是干干凈凈的虑省,不要繼承一堆亂七八糟的東西。所以呢僧凰,不如給SystemServer和應(yīng)用進程里都要用到的資源抽出來單獨放在一個進程里探颈,也就是這的zygote進程,然后zygote進程再分別孵化出SystemServer進程和應(yīng)用進程训措。孵化出來之后伪节,SystemServer進程和應(yīng)用進程就可以各干各的事了光羞。

  • Zygote的IPC通信機制為什么不采用binder?如果采用binder的話會有什么問題么怀大?

第一個原因纱兑,我們可以設(shè)想一下采用binder調(diào)用的話該怎么做,首先zygote要啟用binder機制化借,需要打開binder驅(qū)動潜慎,獲得一個描述符,再通過mmap進行內(nèi)存映射蓖康,還要注冊binder線程铐炫,這還不夠,還要創(chuàng)建一個binder對象注冊到serviceManager蒜焊,另外AMS要向zygote發(fā)起創(chuàng)建應(yīng)用進程請求的話驳遵,要先從serviceManager查詢zygote的binder對象,然后再發(fā)起binder調(diào)用山涡,這來來回回好幾趟非常繁瑣堤结,相比之下,zygote和SystemServer進程本來就是父子關(guān)系鸭丛,對于簡單的消息通信竞穷,用管道或者socket非常方便省事。第二個原因鳞溉,如果zygote啟用binder機制瘾带,再fork出SystemServer,那么SystemServer就會繼承了zygote的描述符以及映射的內(nèi)存熟菲,這兩個進程在binder驅(qū)動層就會共用一套數(shù)據(jù)結(jié)構(gòu)看政,這顯然是不行的,所以還得先給原來的舊的描述符關(guān)掉抄罕,再重新啟用一遍binder機制允蚣,這個就是自找麻煩了。

在網(wǎng)上看到一個Zygote工作流程圖呆贿,感覺描述的非常清晰嚷兔,可以參考一下:

好了,關(guān)于Zygote的理解就到這里了做入,有什么不對的地方希望大家指正冒晰!共同進步!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末竟块,一起剝皮案震驚了整個濱河市壶运,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浪秘,老刑警劉巖蒋情,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件埠况,死亡現(xiàn)場離奇詭異,居然都是意外死亡恕出,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門违帆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浙巫,“玉大人,你說我怎么就攤上這事刷后〉某耄” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵尝胆,是天一觀的道長丧裁。 經(jīng)常有香客問我,道長含衔,這世上最難降的妖魔是什么煎娇? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮贪染,結(jié)果婚禮上缓呛,老公的妹妹穿的比我還像新娘。我一直安慰自己杭隙,他們只是感情好哟绊,可當我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痰憎,像睡著了一般票髓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上铣耘,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天洽沟,我揣著相機與錄音,去河邊找鬼蜗细。 笑死玲躯,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的鳄乏。 我是一名探鬼主播跷车,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼橱野!你這毒婦竟也來了朽缴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤水援,失蹤者是張志新(化名)和其女友劉穎密强,沒想到半個月后茅郎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡或渤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年系冗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薪鹦。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡掌敬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出池磁,到底是詐尸還是另有隱情奔害,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布地熄,位于F島的核電站华临,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏端考。R本人自食惡果不足惜雅潭,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望却特。 院中可真熱鬧寻馏,春花似錦、人聲如沸核偿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漾岳。三九已至轰绵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尼荆,已是汗流浹背左腔。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留捅儒,地道東北人液样。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像巧还,于是被迫代替她去往敵國和親鞭莽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,554評論 2 349