[深刻理解Android卷一全文-第四章]深刻理解zygote

因爲《深刻理解Android 卷一》和《深刻理解Android卷二》再也不出版,而知識的傳播不該該由於紙質媒介的問題而中斷,因此我將在OSC博客中全文轉發這兩本書的所有內容。

第4章  深刻理解Zygote

本章主要內容 html

·  深刻分析zygote,並介紹system_server進程的初始化工做。 java

本章涉及的源代碼文件名及位置 android

下面是咱們本章分析的源碼文件名及其位置。 shell

·  App_main.cpp 編程

framework/base/cmds/app_process/App_main.cpp 數組

·  AndroidRuntime.h 網絡

framework/base/include/android_runtime/AndroidRuntime.h app

·  android_debug_JNITest.cpp 異步

framework/base/core/jni/android_debug_JNITest.cpp socket

·  ZygoteInit.java

framework/base/core/java/com/android/internal/os/ZygoteInit.java

·  dalvik_system_Zygote.c

dalvik/vm/native/dalvik_system_Zygote.c

·  RuntimeInit.java

framework/base/core/java/com/android/internal/os/RuntimeInit.java

·  SystemServer.java

framework/base/services/java/com/android/server/SystemServer.java

·  com_android_server_SystemServer.cpp

framework/base/services/jni/com_android_server_SystemServer.cpp

·  system_init.cpp

framework/base/cmds/system_server/library/system_init.cpp

·  Watchdog.java

framework/base/services/java/com/android/server/Watchdog.java

·  ActivityManagerService.java

framework/base/services/java/com/android/server/am/ActivityManagerService.java

·  Process.java

framework/base/core/java/android/os/Process.java

·  ZygoteConnection.java

framework/base/core/java/com/android/internal/os/ZygoteConnection.java

 

4.1  綜述

讀者可能已經知道,Android系統存在着兩個徹底不一樣的世界:

·  Java世界,Google放出的SDK主要就是針對這個世界的。在這個世界中運行的程序都是基於Dalvik虛擬機的Java程序。

·  Native世界,也就是用Native語言C或C++開發的程序,它們組成了Native世界。

初次接觸Android的人,可能會有幾個疑問:

·  Android是基於Linux內核構建的,它最先存在的確定是Native世界,那麼Java世界是何時建立的呢?

·  咱們都知道,程序運行時必定要有一個進程,可是咱們在編寫Activity、Service的時候卻絕少接觸到「進程」這一律念。固然這是Google有意爲之,但這些Activity或Service卻又不能脫離「進程」而存在。那麼,這個「進程」是怎麼建立和運行的呢?這是一個值得琢磨的問題。

·  在程序中,咱們常用系統的Service,那麼,這些Service在哪裏呢?

這些問題的答案都和咱們本章的兩位主人公zygote和system_server有關。zygote這個詞的中文意思是「受精卵」,它和Android系統中的Java世界有着重要關係。而system_server則「人如其名」,系統中重要的service都駐留於Java中。

zygote和system_server這兩個進程分別是Java世界的半邊天,任何一個進程的死亡,都會致使Java世界的崩潰,夠厲害吧?下面咱們就來見識見識這兩個重量級人物。

 

4.2  Zygote分析

Zygote自己是一個Native的應用程序,和驅動、內核等均無關係。根據第3章對於init的介紹咱們能夠知道,Zygote是由init進程根據init.rc文件中的配置項而建立的。在分析它以前,咱們有必要先簡單介紹一下「zygote」這個名字的來歷。zygote最初的名字叫「app_process」,這個名字是在Android.mk文件中被指定的,但app_process在運行過程當中,經過Linux下的pctrl系統調用將本身的名字換成了「zygote」,因此咱們經過ps命令看到的進程名是「zygote」。

zygote玩的這一套「換名把戲」並不影響咱們的分析,它的原型app_process所對應的源文件是App_main.cpp,代碼以下所示:

[-->App_main.cpp]

int main(int argc, const char* const argv[])

{

  /*

   Zygote進程由init經過fork而來,咱們回顧一下init.rc中設置的啓動參數:

    -Xzygote/system/bin --zygote --start-system-server

  */

    mArgC= argc;

    mArgV= argv;

   

   mArgLen = 0;

    for(int i=0; i<argc; i++) {

       mArgLen += strlen(argv[i]) + 1;

    }

   mArgLen--;

   AppRuntime runtime;

    // 調用Appruntime的addVmArguments,這個函數很簡單,讀者能夠自行分析

    int i= runtime.addVmArguments(argc, argv);

    if (i< argc) {

       //設置runtime的mParentDir爲/system/bin

       runtime.mParentDir = argv[i++];

    }

 

    if (i< argc) {

       arg = argv[i++];

        if(0 == strcmp("--zygote", arg)) {

          //咱們傳入的參數知足if的條件,並且下面的startSystemServer的值爲true

           bool startSystemServer = (i < argc) ?

                    strcmp(argv[i],"--start-system-server") == 0 : false;

           setArgv0(argv0, "zygote");

//設置本進程名爲zygote,這正是前文所講的「換名把戲」。

            set_process_name("zygote");

                 //①調用runtime的start,注意第二個參數startSystemServer爲true

           runtime.start("com.android.internal.os.ZygoteInit",

                              startSystemServer);

        }

        ......

    }

         ......

}

Zygote的這個main函數雖很簡單,但其重要功能倒是由AppRuntime的start來完成的。下面,咱們就來具體分析這個AppRuntime。

4.2.1  AppRuntime分析

AppRuntime類的聲明和實現均在App_main.cpp中,它是從AndroidRuntime類派生出來的,圖4-1顯示了這兩個類的關係和一些重要函數:

圖4-1  AppRuntime和AndroidRuntime的關係

由上圖咱們可知:

·  AppRuntime重載了onStarted、onZygoteInit和onExit函數。

前面的代碼中調用了AndroidRuntime的start函數,由圖4-1可知,這個start函數使用的是基類AndroidRuntime的start,咱們來分析一下它,注意它的調用參數。

[-->AndroidRuntime.cpp]

void AndroidRuntime::start(const char*className, const bool startSystemServer)

{

    //className的值是"com.android.internal.os.ZygoteInit"

    //startSystemServer的值是true

    char*slashClassName = NULL;

    char*cp;

   JNIEnv* env;

    blockSigpipe();//處理SIGPIPE信號

     ......

 

    constchar* rootDir = getenv("ANDROID_ROOT");

if (rootDir == NULL) {

//若是環境變量中沒有ANDROID_ROOT,則新增該變量,並設置值爲「/system"

       rootDir = 「/system";

        ......

       setenv("ANDROID_ROOT", rootDir, 1);

    }

 

    //① 建立虛擬機

    if(startVm(&mJavaVM, &env) != 0)

        goto bail;

 

     //②註冊JNI函數

    if(startReg(env) < 0) {

        goto bail;

      }

 

    jclassstringClass;

   jobjectArray strArray;

   jstring classNameStr;

   jstring startSystemServerStr;

 

   stringClass = env->FindClass("java/lang/String");

    //建立一個有兩個元素的String數組,即Java代碼 String strArray[] = new String[2]

   strArray = env->NewObjectArray(2, stringClass, NULL);

 

  classNameStr = env->NewStringUTF(className);

   //設置第一個元素爲"com.android.internal.os.ZygoteInit"

   env->SetObjectArrayElement(strArray, 0, classNameStr);

   startSystemServerStr = env->NewStringUTF(startSystemServer ?

                                                "true" : "false");

   //設置第二個元素爲"true",注意這兩個元素都是String類型,即字符串。

   env->SetObjectArrayElement(strArray, 1, startSystemServerStr);

 

    jclassstartClass;

   jmethodID startMeth;

 

   slashClassName = strdup(className);

   /*

     將字符串「com.android.internal.os.ZygoteInit」中的「. 」換成「/」,

     這樣就變成了「com/android/internal/os/ZygoteInit」,這個名字符合JNI規範,

     咱們可將其簡稱爲ZygoteInit類。

   */

    for(cp = slashClassName; *cp != '\0'; cp++)

        if(*cp == '.')

           *cp = '/';

 

   startClass = env->FindClass(slashClassName);

    ......

    //找到ZygoteInit類的static main函數的jMethodId。

   startMeth = env->GetStaticMethodID(startClass, "main",

                                             "([Ljava/lang/String;)V");

     ......

     /*

        ③經過JNI調用Java函數,注意調用的函數是main,所屬的類是

          com.android.internal.os.ZygoteInit,傳遞的參數是

          「com.android.internal.os.ZygoteInit true」,

          調用ZygoteInit的main函數後,Zygote便進入了Java世界!

          也就是說,Zygote是開創Android系統中Java世界的盤古。

     */

      env->CallStaticVoidMethod(startClass,startMeth, strArray);

    //Zygote退出,在正常狀況下,Zygote不須要退出。

    if(mJavaVM->DetachCurrentThread() != JNI_OK)

       LOGW("Warning: unable to detach main thread\n");

    if(mJavaVM->DestroyJavaVM() != 0)

       LOGW("Warning: VM did not shut down cleanly\n");

 

bail:

   free(slashClassName);

}

經過上面的分析,咱們找到了三個關鍵點,它們共同組成了開創Android系統中Java世界的三部曲。如今讓咱們來具體地觀察它們。

1. 建立虛擬機——startVm

咱們先看三部曲中的第一部:startVm,這個函數沒有特別之處,就是調用JNI的虛擬機建立函數,可是虛擬機建立時的一些參數倒是在startVm中被肯定的,其代碼以下所示:

[-->AndroidRuntime.cpp]

int AndroidRuntime::startVm(JavaVM** pJavaVM,JNIEnv** pEnv)

{

   //這個函數絕大部分代碼都是設置虛擬機的參數,咱們只分析其中的兩個。

  /*

      下面的代碼是用來設置JNI check選項的。JNIcheck 指的是Native層調用JNI函數時,

系統所作的一些檢查工做。例如調用NewUTFString函數時,系統會檢查傳入的字符串是否是符合

     UTF-8的要求。JNI check還能檢查資源是否正確釋放。但這個選項也有其反作用,好比:

     1)由於檢查工做比較耗時,因此會影響系統運行速度。

     2)有些檢查過於嚴格,例如上面的字符串檢查,一旦出錯,則調用進程就會abort。

        因此,JNI check選項通常只在調試的eng版設置,而正式發佈的user版則不設置該選項。

    下面這幾句代碼就控制着是否啓用JNI check,這是由系統屬性決定的,eng版如通過特殊配置,也能夠去掉JNI check。

   */

    property_get("dalvik.vm.checkjni",propBuf, "");

    if(strcmp(propBuf, "true") == 0) {

       checkJni = true;

    } elseif (strcmp(propBuf, "false") != 0) {

       property_get("ro.kernel.android.checkjni",propBuf, "");

        if(propBuf[0] == '1') {

           checkJni = true;

        }

        }

    ......

   /*

設置虛擬機heapsize,默認爲16MB。絕大多數廠商都會修改這個值,通常是32MB。

heapsize不能設置太小,不然在操做大尺寸的圖片時沒法分配所需內存。

     這裏有一個問題,即heapsize既然是系統級的屬性,那麼可否根據不一樣應用程序的需求來進行動

         態調整?我開始也考慮過可否實現這一構想,不過但願很快就破滅了。對這一問題,咱們將在拓展

         部分深刻討論。

    */

   strcpy(heapsizeOptsBuf, "-Xmx");

   property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");

   opt.optionString = heapsizeOptsBuf;

   mOptions.add(opt);

 

    if(checkJni) {

        opt.optionString ="-Xcheck:jni";

       mOptions.add(opt);

        //JNIcheck中的資源檢查,系統中建立的Globalreference個數不能超過2000

       opt.optionString = "-Xjnigreflimit:2000";

       mOptions.add(opt);

   }

    // 調用JNI_CreateJavaVM建立虛擬機,pEnv返回當前線程的JNIEnv變量

   if(JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {

       LOGE("JNI_CreateJavaVM failed\n");

       goto bail;

    }

 

    result= 0;

 

bail:

   free(stackTraceFile);

    returnresult;

}

關於dalvik虛擬機的詳細參數,讀者能夠參見Dalvik/Docs/Dexopt.html中的說明。這個Docs目錄下的內容,或許可幫助咱們更深刻地瞭解dalvik虛擬機。

 

2. 註冊JNI函數——startReg

前面已經介紹瞭如何建立虛擬機,下一步則須要給這個虛擬機註冊一些JNI函數。正是由於後續Java世界用到的一些函數是採用native方式來實現的,因此才必須提早註冊這些函數。

下面咱們來看看這個startReg函數,代碼以下所示:

[-->AndroidRuntime.cpp]

int AndroidRuntime::startReg(JNIEnv* env)

{

 //注意,設置Thread類的線程建立函數爲javaCreateThreadEtc

 //它的做用,將在對Thread分析一部分(第5章)中作詳細介紹。

 androidSetCreateThreadFunc((android_create_thread_fn)javaCreateThreadEtc);

 

  env->PushLocalFrame(200);

  //註冊jni函數,gRegJNI是一個全局數組。

  if(register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {

       env->PopLocalFrame(NULL);

       return -1;

    }

   env->PopLocalFrame(NULL);

    //下面這句話應當是「碼農」休閒時的小把戲。在突飛猛進的IT世界中,它現已絕對是「文物」了。

   //createJavaThread("fubar", quickTest, (void*)"hello");

    return0;

}

咱們來看看register_jni_procs,代碼以下所示:

[-->AndroidRuntime.cpp]

static int register_jni_procs(const RegJNIRecarray[], size_t count, JNIEnv* env)

{

    for(size_t i = 0; i < count; i++) {

        if(array[i].mProc(env) < 0) {//僅僅是一個封裝,調用數組元素的mProc函數

       return -1;

        }

        }

        return 0;

}

上面的函數調用的不過是數組元素的mProc函數,再讓咱們直接看看這個全局數組的gRegJNI變量。

[-->AndroidRuntime.cpp::gRegJNI聲明]

static const RegJNIRec gRegJNI[] = {

   REG_JNI(register_android_debug_JNITest),

   REG_JNI(register_com_android_internal_os_RuntimeInit),

   REG_JNI(register_android_os_SystemClock),

   REG_JNI(register_android_util_EventLog),

   REG_JNI(register_android_util_Log),

     ...//共有100項

};

REG_JNI是一個宏,宏裏邊包括的就是那個mProc函數,這裏,咱們來分析一個例子。

[-->android_debug_JNITest.cpp]

int register_android_debug_JNITest(JNIEnv* env)

{

   //爲android.debug.JNITest類註冊它所須要的JNI函數

   returnjniRegisterNativeMethods(env, "android/debug/JNITest",

                                          gMethods,NELEM(gMethods));

}

哦,原來mProc就是爲Java類註冊JNI函數!

至此,虛擬機已建立好,JNI函數也已註冊,下一步就要分析CallStaticVoidMethod了。經過這個函數,咱們將進入Android所精心打造的Java世界,並且最佳狀況是,永遠也不回到Native世界。

4.2.2  Welcome to Java World

這個Java世界的入口在哪裏?根據前面的分析,CallStaticVoidMethod最終將調用com.android.internal.os.ZygoteInit的main函數,下面就來看看這個入口函數。代碼以下所示:

[-->ZygoteInit.java]

public static void main(String argv[]) {

  try {

        

       SamplingProfilerIntegration.start();

       //①註冊Zygote用的socket

       registerZygoteSocket();

       //②預加載類和資源

       preloadClasses();

       preloadResources();

       ......

       // 強制一次垃圾收集

       gc();

       

      //咱們傳入的參數知足if分支

      if (argv[1].equals("true")) {

          startSystemServer();//③啓動system_server進程

       }else if (!argv[1].equals("false")) {

          thrownew RuntimeException(argv[0] + USAGE_STRING);

        }

      // ZYGOTE_FORK_MODE被定義爲false,因此知足else的條件

       if(ZYGOTE_FORK_MODE) {

            runForkMode();

       }else {

          runSelectLoopMode();//④zygote調用這個函數

       }

       closeServerSocket();//關閉socket

        }catch (MethodAndArgsCaller caller) {

           caller.run();//⑤很重要的caller run函數,之後分析

        }catch (RuntimeException ex) {

          closeServerSocket();

           throw ex;

        }

     ......

    }

在ZygoteInit的main函數中,咱們列舉出了5大關鍵點,下面對其一一進行分析。先看第一點:registerZygoteSocket。

1. 創建IPC通訊服務端——registerZygoteSocket

Zygote以及系統中其餘程序的通訊沒有使用Binder,而是採用了基於AF_UNIX類型的Socket。registerZygoteSocket函數的使命正是創建這個Socket。代碼以下所示:

[-->ZygoteInit.java]

private static void registerZygoteSocket() {

    if(sServerSocket == null) {

        intfileDesc;

        try{

           //從環境變量中獲取Socket的fd,還記得第3章init中介紹的zygote是如何啓動的嗎?

//這個環境變量由execv傳入。

          String env = System.getenv(ANDROID_SOCKET_ENV);

          fileDesc = Integer.parseInt(env);

       }

       try{

         //建立服務端Socket,這個Socket將listen並accept Client

         sServerSocket= new LocalServerSocket(createFileDescriptor(fileDesc));

       }

       }

}

registerZygoteSocket很簡單,就是建立一個服務端的Socket。不過讀者應該提早想到下面兩個問題:

·  誰是客戶端?

·  服務端會怎麼處理客戶端的消息?

建議:讀者要好好學習與Socket相關的知識,這些知識對網絡編程或簡單的IPC使用,是會有幫助的。

2. 預加載類和資源

如今咱們要分析的就是preloadClasses和preloadResources函數了。先來看看preloadClasses。

[-->ZygoteInit.java]

private static void preloadClasses() {

     finalVMRuntime runtime = VMRuntime.getRuntime();

     //預加載類的信息存儲在PRELOADED_CLASSES變量中,它的值爲"preloaded-classes"

    InputStream is = ZygoteInit.class.getClassLoader().getResourceAsStream(

                           PRELOADED_CLASSES);

        if(is == null) {

           Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES +".");

        }else {

              ...... //作一些統計和準備工做

 

            try {

               BufferedReader br

                    = new BufferedReader(newInputStreamReader(is), 256);

               //讀取文件的每一行,忽略#開頭的註釋行

               int count = 0;

               String line;

               String missingClasses = null;

               while ((line = br.readLine()) != null) {

                      line = line.trim();

                    if(line.startsWith("#") || line.equals("")) {

                        continue;

                    }

 

                    try {

                        //經過Java反射來加載類,line中存儲的是預加載的類名

                        Class.forName(line);

                       ......

                        count++;

                    } catch(ClassNotFoundException e) {

                       ......

                   } catch (Throwable t) {

                        ......

                    }

                   }

               ...... //掃尾工做

        }

        }

preloadClasses看起來是如此簡單,可是你知道它有多少個類須要預先加載嗎?

用coolfind在framework中搜索名爲「preloaded-classes」的文件,最後會在framework/base目錄下找到。它是一個文本文件,內容以下:

# Classes which are preloaded bycom.android.internal.os.ZygoteInit.

# Automatically generated by

# frameworks/base/tools/preload/WritePreloadedClassFile.java.

# MIN_LOAD_TIME_MICROS=1250  //超時控制

android.R$styleable

android.accounts.AccountManager

android.accounts.AccountManager$4

android.accounts.AccountManager$6

android.accounts.AccountManager$AmsTask

android.accounts.AccountManager$BaseFutureTask

android.accounts.AccountManager$Future2Task

android.accounts.AuthenticatorDescription

android.accounts.IAccountAuthenticatorResponse$Stub

android.accounts.IAccountManager$Stub

android.accounts.IAccountManagerResponse$Stub

......//一共有1268行

這個preload-class一共有1268行,試想,加載這麼多類得花多少時間!

說明:preload_class文件由framework/base/tools/preload工具生成,它須要判斷每一個類加載的時間是否大於1250微秒,超過這個時間的類就會被寫到preload-classes文件中,最後由zygote預加載。這方面的內容,讀者可參考有關preload工具中的說明,這裏就再也不贅述。

preloadClass函數的執行時間比較長,這是致使Android系統啓動慢的緣由之一。對這一塊能夠作一些優化,但優化是基於對整個系統有比較深刻了解才能實現的。

注意:在拓展思考部分中,咱們會討論Android啓動速度問題。

preloadResources和preloadClass相似,它主要是加載framework-res.apk中的資源。這裏就再也不介紹它了。

說明:在UI編程中常使用的com.android.R.XXX資源,是系統默認的資源,它們就是由Zygote加載的。

3. 啓動system_server

咱們如今要分析的是第三個關鍵點:startSystemServer。這個函數會建立Java世界中系統Service所駐留的進程system_server,該進程是framework的核心。若是它死了,就會致使zygote自殺。先來看看這個核心進程是如何啓動的。

[-->ZygoteInit.java]

private static boolean startSystemServer()

           throws MethodAndArgsCaller, RuntimeException {

        //設置參數

       String args[] = {

            "--setuid=1000",//uid和gid等設置

           "--setgid=1000",

            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,

                            3001,3002,3003",

           "--capabilities=130104352,130104352",

           "--runtime-init",

           "--nice-name=system_server", //進程名,叫system_server

           "com.android.server.SystemServer", //啓動的類名

        };

       ZygoteConnection.Arguments parsedArgs = null;

       int pid;

       try {

          //把上面字符串數組參數轉換成Arguments對象。具體內容請讀者自行分析。

           parsedArgs = new ZygoteConnection.Arguments(args);

           int debugFlags = parsedArgs.debugFlags;

          //fork一個子進程,看來,這個子進程就是system_server進程。

           pid = Zygote.forkSystemServer(

                    parsedArgs.uid,parsedArgs.gid,

                    parsedArgs.gids,debugFlags, null);

        }catch (IllegalArgumentException ex) {

           throw new RuntimeException(ex);

        }

     /*

      關於fork的知識,請讀者務花些時間去研究。若是對fork具體實現還感興趣,可參考

     《Linux內核源代碼情景分析》一書。(該書由浙江大學出版社出版,做者爲毛德操、胡希明)

      下面代碼中,若是pid爲零,則表示處於子進程中,也就是處於system_server進程中。

     */

        if(pid == 0) {

         //① system_server進程的工做

           handleSystemServerProcess(parsedArgs);

        }

       //zygote返回true

       return true;

    }

OK,這裏出現了一個分水嶺,即Zygote進行了一次無性繁殖,分裂出了一個system_server進程。關於它的故事,咱們會在後文作專門分析,這裏先說Zygote。

4. 有求必應之等待請求——runSelectLoopMode

當Zygote從startSystemServer返回後,將進入第四個關鍵函數:runSelectLoopMode。前面,在第一個關鍵點registerZygoteSocket中註冊了一個用於IPC的Socket,不過那時尚未地方用到它。它的用途將在這個runSelectLoopMode中體現出來,請看下面的代碼:

[-->ZygoteInit.java]

private static void runSelectLoopMode()

throws MethodAndArgsCaller {

       ArrayList<FileDescriptor> fds = new ArrayList();

       ArrayList<ZygoteConnection> peers = new ArrayList();

       FileDescriptor[] fdArray = new FileDescriptor[4];

      //sServerSocket是咱們先前在registerZygoteSocket創建的Socket

       fds.add(sServerSocket.getFileDescriptor());

       peers.add(null);

 

       int loopCount = GC_LOOP_COUNT;

       while (true) {

           int index;

             try {

               fdArray = fds.toArray(fdArray);

        /*

          selectReadable內部調用select,使用多路複用I/O模型。

          當有客戶端鏈接或有數據時,則selectReadable就會返回。

        */

              index = selectReadable(fdArray);

           }

          else if (index == 0) {

             //若有一個客戶端鏈接上,請注意客戶端在Zygote的表明是ZygoteConnection

               ZygoteConnection newPeer = acceptCommandPeer();

               peers.add(newPeer);

               fds.add(newPeer.getFileDesciptor());

           } else {

               boolean done;

              //客戶端發送了請求,peers.get返回的是ZygoteConnection

             //後續處理將交給ZygoteConnection的runOnce函數完成。

               done = peers.get(index).runOnce();

        }

    }

runSelectLoopMode比較簡單,就是:

·  處理客戶鏈接和客戶請求。其中客戶在Zygote中用ZygoteConnection對象來表示。

·  客戶的請求由ZygoteConnection的runOnce來處理。

建議:runSelectLoopMode比較簡單,但它使用的select的背後所表明的思想卻並不是簡單。建議讀者以此爲契機,認真學習經常使用的I/O模型,包括阻塞式、非阻塞式、多路複用、異步I/O等,掌握這些知識,對於將來編寫大型系統頗有幫助。

關於Zygote是如何處理請求的,將單獨用一節內容進行討論。

4.2.3 關於 Zygote的總結

Zygote是建立Android系統中Java世界的盤古,它建立了第一個Java虛擬機,同時它又是女媧,它成功地繁殖了framework的核心system_server進程。作爲Java語言的受益者,咱們理應回顧一下Zygote建立Java世界的步驟:

·  第一天:建立AppRuntime對象,並調用它的start。此後的活動則由AppRuntime來控制。

·  次日:調用startVm建立Java虛擬機,而後調用startReg來註冊JNI函數。

·  第三天:經過JNI調用com.android.internal.os.ZygoteInit類的main函數,今後進入了Java世界。然而在這個世界剛開創的時候,什麼東西都沒有。

·  第四天:調用registerZygoteSocket。經過這個函數,它能夠響應子孫後代的請求。同時Zygote調用preloadClasses和preloadResources,爲Java世界添磚加瓦。

·  第五天:Zygote以爲本身工做壓力太大,便經過調用startSystemServer分裂一個子進程system_server來爲Java世界服務。

·  第六天:Zygote完成了Java世界的初創工做,它已經很知足了。下一步該作的就是調用runSelectLoopMode後,便沉沉地睡去了。

·  之後的日子:Zygote隨時守護在咱們的周圍,當接收到子孫後代的請求時,它會隨時醒來,爲它們工做。

若是支持中文編碼的話,我必定要爲Zygote取名爲盤古_女媧。

4.3  SystemServer分析

SystemServer的進程名實際上叫作「system_server」,這裏咱們可將其簡稱爲SS。SS作爲Zygote的嫡長子,其重要性不言而喻。關於這一點,經過代碼分析即可立刻知曉。

4.3.1  SystemServer的誕生

咱們先回顧一下SS是怎麼建立的。

String args[] = {

           "--setuid=1000",

           "--setgid=1000",

           "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,

                             3001,3002,3003",

           "--capabilities=130104352,130104352",

           "--runtime-init",

           "--nice-name=system_server",

           "com.android.server.SystemServer",

        };

       ZygoteConnection.Arguments parsedArgs = null;

 

       int pid;

      parsedArgs = new ZygoteConnection.Arguments(args);

       intdebugFlags = parsedArgs.debugFlags;

       pid = Zygote.forkSystemServer(  //調用forkSystemServer

                    parsedArgs.uid,parsedArgs.gid,

                    parsedArgs.gids,debugFlags, null);

 

從上面的代碼中能夠看出,SS是由Zygote經過Zygote.forkSystemServer函數fork誕生出來的。這裏會有什麼玄機嗎?先來一塊兒看看forkSystemServer的實現。它是一個native函數,實如今dalvik_system_Zygote.c中,以下所示:

[-->dalvik_system_Zygote.c]

static voidDalvik_dalvik_system_Zygote_forkSystemServer(

                        const u4* args, JValue* pResult)

{

     pid_tpid;

    //根據參數,fork一個子進程

    pid =forkAndSpecializeCommon(args);

    if (pid > 0) {

       int status;

       gDvm.systemServerPid = pid;//保存system_server的進程id

      //函數退出前須先檢查剛建立的子進程是否退出了。

        if(waitpid(pid, &status, WNOHANG) == pid) {

           //若是system_server退出了,Zygote直接幹掉了本身

           //看來Zygote和SS的關係異常緊密,簡直是生死與共!

            kill(getpid(), SIGKILL);

        }

    }

   RETURN_INT(pid);

}

下面,再看看forkAndSpecializeCommon,代碼以下所示:

[-->dalvik_system_Zygote.c]

static pid_t forkAndSpecializeCommon(const u4*args)

{

    pid_tpid;

    uid_tuid = (uid_t) args[0];

    gid_tgid = (gid_t) args[1];

   ArrayObject* gids = (ArrayObject *)args[2];

    u4debugFlags = args[3];

   ArrayObject *rlimits = (ArrayObject *)args[4];

   //設置信號處理,待會兒要看看這個函數。  

   setSignalHandler();     

    pid =fork(); //fork子進程

   if (pid== 0) {

     //對子進程要根據傳入的參數作一些處理,例如設置進程名,設置各類id(用戶id,組id等)

   }

......

}

最後看看setSignalHandler函數,它由Zygote在fork子進程前調用,代碼以下所示:

[-->dalvik_system_Zygote.c]

static void setSignalHandler()

{

    interr;

    structsigaction sa;

   memset(&sa, 0, sizeof(sa));

   sa.sa_handler = sigchldHandler;

    err =sigaction (SIGCHLD, &sa, NULL);//設置信號處理函數,該信號是子進程死亡的信號

}

//咱們直接看這個信號處理函數sigchldHandler

static void sigchldHandler(int s)

{

    pid_tpid;

    intstatus;

    

    while((pid = waitpid(-1, &status, WNOHANG)) > 0) {

             } else if (WIFSIGNALED(status)) {

          }

        }

        //若是死去的子進程是SS,則Zygote把本身也幹掉了,這樣就作到了生死與共!

        if(pid == gDvm.systemServerPid) {

           kill(getpid(), SIGKILL);

        }

   }

OK,作爲Zygote的嫡長子,SS確實具備很是高的地位,居然到了與Zygote生死與共的地步!它爲何這麼重要呢?咱們如今就從forkSystemServer來分析SS究竟承擔了怎樣的工做使命。

關於源代碼定位的問題,很多人當面對浩瀚的代碼時,經常不知道具體函數是在哪一個文件中定義的。這裏,就Source insight的使用提幾點建議:

   1)加入工程的時候,不要把全部目錄所有加進去,不然會致使解析速度異常緩慢。咱們能夠先加入framework目錄,如之後另有須要時,再加入其餘目錄。

2)除了Sourceinsight的工具外,還須要有一個能搜索文件中特定字符串的工具,我用的是coolfind。forkSystemServer這個函數,就是經過它在源碼中搜索到的,而且找到了實現文件dalvik_system_Zygote.c。在Linux下也有對應工具,但工做速度比coolfind緩慢。

3) 在Linux下,可經過wine(一個支持Linux平臺安裝Windows軟件的工具)安裝Source insight。

4.3.2  SystemServer的重要使命

SS誕生後,便和生父Zygote分道揚鑣,它有了本身的歷史使命。它的使命是什麼呢?其代碼以下所示:

    pid =Zygote.forkSystemServer();

     if(pid == 0) { //SS進程返回0,那麼下面這句話就是SS的使命:

           handleSystemServerProcess(parsedArgs);

    }

SS調用handleSystemServerProcess來承擔本身的職責。

[-->ZygoteInit.java]

private static void handleSystemServerProcess(

       ZygoteConnection.ArgumentsparsedArgs)

      throws ZygoteInit.MethodAndArgsCaller {

        //關閉從Zygote那裏繼承下來的Socket。 

        closeServerSocket();

      //設置SS進程的一些參數。

        setCapabilities(parsedArgs.permittedCapabilities,

                   parsedArgs.effectiveCapabilities);

        //調用ZygoteInit函數。

        RuntimeInit.zygoteInit(parsedArgs.remainingArgs);

  }

 好了,SS走到RuntimeInit了,它的代碼在RuntimeInit.java中,以下所示:   

[-->RuntimeInit.java]

public static final void zygoteInit(String[]argv)

           throws ZygoteInit.MethodAndArgsCaller {

     //作一些常規初始化

     commonInit();

     //①native層的初始化。

    zygoteInitNative();

     intcurArg = 0;

     for (/* curArg */ ; curArg < argv.length; curArg++) {

           String arg = argv[curArg];

 

           if (arg.equals("--")) {

               curArg++;

               break;

           } else if (!arg.startsWith("--")) {

               break;

           } else if (arg.startsWith("--nice-name=")) {

               String niceName = arg.substring(arg.indexOf('=') + 1);

               //設置進程名爲niceName,也就是"system_server"

               Process.setArgV0(niceName);

           }

        }

       //startClass名爲"com.android.server.SystemServer"

       String startClass = argv[curArg++];

       String[] startArgs = new String[argv.length - curArg];

       System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);

       //②調用startClass,也就是com.android.server.SystemServer類的main函數。

       invokeStaticMain(startClass, startArgs);

}

對於上面列舉出的兩個關鍵點,咱們一個一個地分析。

1. zygoteInitNative分析

先看zygoteInitNative,它是一個native函數,實如今AndroidRuntime.cpp中。

[-->AndroidRuntime.cpp]

static voidcom_android_internal_os_RuntimeInit_zygoteInit(

JNIEnv* env,jobject clazz)

{

   gCurRuntime->onZygoteInit();

}

//gCurRuntime是什麼?還記得咱們在本章開始說的app_process的main函數嗎?

int main(int argc, const char* const argv[])

{

  AppRuntime runtime;// 就是這個。當時咱們沒顧及它的構造函數,如今回過頭看看。

}

//AppRuntime的定義

class AppRuntime : public AndroidRuntime

static AndroidRuntime* gCurRuntime = NULL; // gCurRuntime爲全局變量。

AndroidRuntime::AndroidRuntime()

{

   SkGraphics::Init();//Skia庫初始化

   SkImageDecoder::SetDeviceConfig(SkBitmap::kRGB_565_Config);

   SkImageRef_GlobalPool::SetRAMBudget(512 * 1024);

    gCurRuntime= this; //gCurRuntime被設置爲AndroidRuntime對象本身

}

因爲SS是從Zygote fork出來的,因此它也擁有Zygote進程中定義的這個gCurRuntime,也就是AppRuntime對象。那麼,它的onZygoteInit會幹些什麼呢?它的代碼在App_main.cpp中,咱們一塊兒來看:

[-->App_main.cpp]

   virtual void onZygoteInit()

    {

        //下面這些東西和Binder有關係,但讀者能夠先無論它。

       sp<ProcessState> proc = ProcessState::self();

        if(proc->supportsProcesses()) {

            proc->startThreadPool();//啓動一個線程,用於Binder通訊。

       }      

}

一言以蔽之,SS調用zygoteInitNative後,將和Binder通訊系統創建聯繫,這樣SS就可以使用Binder了。關於Binder的知識,在第6章中將詳細介紹,讀者朋友如今沒必要關注。

2. invokeStaticMain分析

再來看第二個關鍵點invokeStaticMain。代碼以下所示:

[-->RuntimeInit.java]

private static void invokeStaticMain(StringclassName, String[] argv)

           throws ZygoteInit.MethodAndArgsCaller {

 

      ......//注意咱們的參數,className爲"com.android.server.SystemServer"

       Class<?> cl;

 

       try {

           cl = Class.forName(className);

        }catch (ClassNotFoundException ex) {

           throw new RuntimeException(

                    "Missing class wheninvoking static main " + className,

                    ex);

        }

 

       Method m;

       try {

           //找到com.android.server.SystemServer類的main函數,確定有地方要調用它

           m = cl.getMethod("main", new Class[] { String[].class });

        }catch (NoSuchMethodException ex) {

           ......

        }catch (SecurityException ex) {

           ......

        }

 

       int modifiers = m.getModifiers();

        if(! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {

           ......

        }

        //拋出一個異常,爲何不在這裏直接調用上面的main函數呢?

       throw new ZygoteInit.MethodAndArgsCaller(m, argv);

    }

invokeStaticMain居然拋出了一個異常,它是在哪裏被截獲呢?原來是在ZygoteInit的main函數中。請看這段代碼:

注意:咱們所在的進程是system_server。

[-->ZygoteInit.java]

     ....

if (argv[1].equals("true")) {

     //SS進程中,拋出一個異常MethodAndArgsCaller

    startSystemServer();

     ......

    catch(MethodAndArgsCaller caller) {

     //被截獲,調用caller的run函數

      caller.run(); 

  }

再來看看MethodAndArgsCaller的run函數。

public void run() {

   try {

   //這個mMethod爲com.android.server.SystemServer的main函數

  mMethod.invoke(null, new Object[] { mArgs });

  } catch(IllegalAccessException ex) {

     ......

  }       

}

拋出的這個異常最後會致使com.android.server.SystemServer類的main函數被調用。不過這裏有一個疑問,爲何不在invokeStaticMain那裏直接調用,而是採用這種拋異常的方式呢?我對這個問題的見解是:

·  這個調用是在ZygoteInit.main中,至關於Native的main函數,即入口函數,位於堆棧的頂層。若是不採用拋異常的方式,而是在invokeStaticMain那裏調用,則會浪費以前函數調用所佔用的一些調用堆棧。

關於這個問題的深層思考,讀者能夠利用fork和exec的知識。對這種拋異常的方式,我我的以爲是對exec的一種近似模擬,由於後續的工做將交給com.android.server.SystemServer類來處理。

3. SystemServer的真面目

ZygoteInit分裂產生的SS,其實就是爲了調用com.android.server.SystemServer的main函數,這簡直就是改頭換面!下面就來看看這個真實的main函數,代碼以下所示:

[-->SystemServer.java]

public static void main(String[] args) {

   ......

    //加載libandroid_servers.so

   System.loadLibrary("android_servers");

  //調用native的init1函數。

  init1(args);

}

其中main函數將加載libandroid_server.so庫,這個庫所包含的源碼文件在文件夾framework/base/services/jni下。

(1)init1分析

init1是native函數,在com_android_server_SystemServer.cpp中實現。來看看它,代碼以下所示:

[-->com_android_server_SystemServer.cpp]

extern "C" int system_init();

 

static voidandroid_server_SystemServer_init1(JNIEnv* env, jobject clazz)

{

   system_init();//調用另一個函數。

}

system_init的實如今system_init.cpp中,它的代碼以下所示:

[-->system_init.cpp]

extern "C" status_t system_init()

{

   //下面這些調用和Binder有關,咱們會在第6章中講述,這裏先沒必要管它。

   sp<ProcessState> proc(ProcessState::self());

   sp<IServiceManager> sm = defaultServiceManager();

   

    sp<GrimReaper>grim = new GrimReaper();

   sm->asBinder()->linkToDeath(grim, grim.get(), 0);

    charpropBuf[PROPERTY_VALUE_MAX];

   property_get("system_init.startsurfaceflinger", propBuf,"1");

    if(strcmp(propBuf, "1") == 0) {

        //SurfaceFlinger服務在system_server進程建立

       SurfaceFlinger::instantiate();

    }

 

     ......

 

    //調用com.android.server.SystemServer類的init2函數

   AndroidRuntime* runtime = AndroidRuntime::getRuntime();

   runtime->callStatic("com/android/server/SystemServer","init2");

   

//下面這幾個函數調用和Binder通訊有關,具體內容在第6章中介紹。

    if (proc->supportsProcesses()) {

        ProcessState::self()->startThreadPool();

       //調用joinThreadPool後,當前線程也加入到Binder通訊的大潮中

       IPCThreadState::self()->joinThreadPool();

       }

    returnNO_ERROR;

}

init1函數建立了一些系統服務,而後把調用線程加入Binder通訊中。不過其間還經過JNI調用了com.android.server.SystemServer類的init2函數,下面就來看看這個init2函數。

(2)init2分析

init2在Java層,代碼在SystemServer.java中,以下所示:

[-->SystemServer.java]

public static final void init2() {

   Threadthr = new ServerThread();

   thr.setName("android.server.ServerThread");

   thr.start();//啓動一個ServerThread

}

啓動了一個ServerThread線程。請直接看它的run函數。這個函數比較長,大概看看它幹了什麼便可。

[-->SystemServer.java::ServerThread的run函數]

public void run(){

             ....

  //啓動Entropy Service

  ServiceManager.addService("entropy",new EntropyService());

  //啓動電源管理服務

  power =new PowerManagerService();

 ServiceManager.addService(Context.POWER_SERVICE, power);

  //啓動電池管理服務。

  battery= new BatteryService(context);

 ServiceManager.addService("battery", battery);

 

   //初始化看門狗,在拓展部分將介紹關於看門狗的知識

   Watchdog.getInstance().init(context,battery, power, alarm,

                               ActivityManagerService.self());

            

  //啓動WindowManager服務

  wm =WindowManagerService.main(context, power,

                    factoryTest !=SystemServer.FACTORY_TEST_LOW_LEVEL);

  ServiceManager.addService(Context.WINDOW_SERVICE,wm);

          

  //啓動ActivityManager服務

  (ActivityManagerService)ServiceManager.getService("activity"))

                    .setWindowManager(wm);

 

  ......//總之,系統各類重要服務都在這裏啓動

   Looper.loop();  //進行消息循環,而後處理消息。關於這部份內容參見第5章。

}

init2函數比較簡單,就是單首創建一個線程,用以啓動系統各項服務,至此,讀者或許能理解SS的重要性了吧?

·  Java世界的核心Service都在這裏啓動,因此它很是重要。

說明:本書不對這些Service作進一步分析,從此有機會再作作專門介紹。

4.3.3 關於 SystemServer的總結

SS曲折的調用流程真讓人眼花繚亂,咱們用圖4-2來展現這一過程:

圖4-2  SystemServer的調用流程

注意:init1函數最終致使進程的主線程加入到Binder通訊的大潮中,關於Binder的知識,在第6章中介紹。

 

4.4  Zygote的分裂

前文已經講道,Zygote分裂出嫡長子system_server後,就經過runSelectLoopMode等待並處理來自客戶的消息,那麼,誰會向Zygote發送消息呢?這裏,以一個Activity的啓動爲例,具體分析Zygote是如何分裂和繁殖的。

4.4.1  ActivityManagerService發送請求

ActivityManagerService也是由SystemServer建立的。假設經過startActivit來啓動一個新的Activity,而這個Activity附屬於一個還未啓動的進程,那麼這個進程該如何啓動呢?先來看看ActivityManagerService中的startProcessLocked函數,代碼以下所示:

[-->ActivityManagerService.java]

private final void startProcessLocked(ProcessRecordapp,

           String hostingType, String hostingNameStr){  

      ......//這個ActivityManagerService類很複雜,有14657行!!!        

   if("1".equals(SystemProperties.get("debug.checkjni"))) {

               debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;

           }

           if("1".equals(SystemProperties.get("debug.assert"))) {

               debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;

           }

    //這個Process類是Android提供的,並不是JDK中的Process類

    intpid = Process.start("android.app.ActivityThread",

                    mSimpleProcessManagement ?app.processName : null, uid, uid,

                    gids, debugFlags, null);

   ......

}

接着來看看Process的start函數,這個Process類是android.os.Process,它的代碼在Process.java中,代碼以下所示:

[-->Process.java]

public static final int start(final StringprocessClass,final String niceName,

int uid, int gid, int[] gids,intdebugFlags,String[] zygoteArgs)

{

   //注意,processClass的值是"android.app.ActivityThread"。

   if(supportsProcesses()) {

       try {

            //調用startViaZygote。

            return startViaZygote(processClass, niceName, uid, gid, gids,

                        debugFlags,zygoteArgs);

          }

     }

}

[-->Process.java::startViaZygote()]

private static int startViaZygote(final StringprocessClass,

     final String niceName,final int uid, finalint gid,final int[] gids,

     intdebugFlags,String[] extraArgs) throws ZygoteStartFailedEx {

     int pid;

     ......//一些參數處理,最後調用zygoteSendArgsAndGetPid函數。

 argsForZygote.add("--runtime-init");//這個參數很重要

    argsForZygote.add("--setuid=" +uid);

   argsForZygote.add("--setgid=" + gid);

    pid =zygoteSendArgsAndGetPid(argsForZygote);

     return pid;

}

[-->Process.java::zygoteSendArgsAndGetPid()]

private static intzygoteSendArgsAndGetPid(ArrayList<String> args)

                                           throwsZygoteStartFailedEx {

 

   intpid;

   // openZygoteSocketIfNeeded?是否是打開了和Zygote通訊的Socket?

  openZygoteSocketIfNeeded();

  

   try {

          //把請求的參數發到Zygote。

           sZygoteWriter.write(Integer.toString(args.size()));

           sZygoteWriter.newLine();

           sZygoteWriter.write(arg);

           sZygoteWriter.newLine();

       }

       //讀取Zygote處理完的結果,便得知是某個進程的pid!

       sZygoteWriter.flush();

       pid= sZygoteInputStream.readInt();

       return pid;

}

[-->Process.java]

private static void openZygoteSocketIfNeeded()throws ZygoteStartFailedEx {

try {

         sZygoteSocket = new LocalSocket();//果然如此!!

        //鏈接Zygote

         sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET,

                               LocalSocketAddress.Namespace.RESERVED));

         sZygoteInputStream

                        = newDataInputStream(sZygoteSocket.getInputStream());

         sZygoteWriter = new BufferedWriter(

                                      new OutputStreamWriter(

                                   sZygoteSocket.getOutputStream()),256);

              }

          }

    }

}

好了,ActivityManagerService終於向Zygote發送請求了。請求的參數中有一個字符串,它的值是「android.app.ActivityThread」。如今該回到Zygote處理請求那塊去看看了。

注意:因爲ActivityManagerService駐留於SystemServer進程中,因此正是SS向Zygote發送了消息。

4.4.2  有求必應之響應請求

前面有一節,題目叫「有求必應之等待請求」,那麼這一節「有求必應之響應請求」會回到ZygoteInit。下面就看看它是如何處理請求的。

[--->ZygoteInit.java]

private static void runSelectLoopMode() throwsMethodAndArgsCaller{

    ......

   try {

          fdArray = fds.toArray(fdArray);

          ......

           else if (index == 0) {

               ZygoteConnection newPeer = acceptCommandPeer();

               peers.add(newPeer);

               fds.add(newPeer.getFileDesciptor());

           } else {

               boolean done;

               //調用ZygoteConnection的runOnce

               done = peers.get(index).runOnce();

        }

    ......

}

每當有請求數據發來時,Zygote都會調用ZygoteConnection的runOnce函數。ZygoteConnection代碼在ZygoteConnection.java文件中,來看看它的runOnce函數:

[-->ZygoteConnection.java]

boolean runOnce() throwsZygoteInit.MethodAndArgsCaller {

    try {

           args = readArgumentList();//讀取SS發送過來的參數

           descriptors = mSocket.getAncillaryFileDescriptors();

        }

          ......

         int pid;

     try {

           parsedArgs = new Arguments(args);

           applyUidSecurityPolicy(parsedArgs, peer);

            //根據函數名,可知Zygote又分裂出了一個子進程。

           pid =Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,

                    parsedArgs.gids,parsedArgs.debugFlags, rlimits);

        }

        ......

        if(pid == 0) {

           //子進程處理,這個子進程是否是咱們要建立的Activity對應的子進程呢?

           handleChildProc(parsedArgs, descriptors, newStderr);

           return true;

        }else {

         //zygote進程

          return handleParentProc(pid, descriptors, parsedArgs);

        }

}

接下來,看看新建立的子進程在handleChildProc中作了些什麼。

[-->ZygoteConnection.java]

private void handleChildProc(ArgumentsparsedArgs,FileDescriptor[] descriptors,

                   PrintStream newStderr) throwsZygoteInit.MethodAndArgsCaller {

 

       ......//根據傳入的參數設置新進程的一些屬性

      //SS發來的參數中有「--runtime-init「,因此parsedArgs.runtimeInit爲true。

      if(parsedArgs.runtimeInit) {

           RuntimeInit.zygoteInit(parsedArgs.remainingArgs);

        }else {

       ......

      }

}

[-->RuntimeInit.java]

public static final void zygoteInit(String[]argv)

           throws ZygoteInit.MethodAndArgsCaller {

        //重定向標準輸出和錯誤輸出

       System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));

       System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));

 

       commonInit();

//下面這個函數爲native函數,最終會調用AppRuntime的onZygoteInit,在那個函數中

//創建了和Binder的關係

       zygoteInitNative();

       int curArg = 0;

        ......

        String startClass = argv[curArg++];

       String[] startArgs = new String[argv.length - curArg];

       System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);

        //最終仍是調用invokeStaticMain函數,這個函數咱們已經見識過了。

       invokeStaticMain(startClass, startArgs);

    }

Zygote分裂子進程後,本身將在handleParentProc中作一些掃尾工做,而後繼續等待請求進行下一次分裂。

這個android.app.ActivityThread類,其實是Android中apk程序所對應的進程,它的main函數就是apk程序的main函數。從這個類的命名(android.app)中也能夠看出些端倪。

經過這一節的分析,讀者能夠想到,Android系統運行的那些apk程序,其父都是zygote。這一點,能夠經過adb shell登陸後,用ps命令查看進程和父進程號來確認。

4.4.3 關於 Zygote分裂的總結

Zygote的分裂由SS控制,這個過程咱們用圖4-3來表示:

圖4-3  Zygote響應請求的過程

說明:這裏借用了UML的時序圖來表達Zygote響應請求的過程。

4.5  拓展思考

4.5.1  虛擬機heapsize的限制

在分析Zygote建立虛擬機的時候,咱們說過系統默認設置的Java虛擬機堆棧最大爲16MB,這個值對於須要使用較大內存的程序(例如圖片處理程序)來講還遠遠不夠。固然,能夠修改這個默認值,例如個人HTC G7就將其修改成32MB了,可是這個改動是全局性的,也就是全部的Java程序都會是這個32MB。咱們能動態配置這個值嗎?例如:

·  設置一個配置文件,每一個進程啓動的時候根據配置文件的參數來設置堆大小。

不過正如前面所說,個人這一美好願望最終破滅了,緣由只有一個:

·  Zygote是經過fork來建立子進程的,Zygote自己設置的信息會被子進程所有繼承,例如Zygote設置的堆棧爲16MB,那麼它的子進程也是用這個16MB。

關於這個問題,我目前想到了兩個解決方案:

·  爲Dalivk增長一個函數,這個函數容許動態調整最大堆的大小。

·  Zygote經過fork子進程後,調用exec家族的函數來加載另一個映像,該映像對應的程序會從新建立虛擬機,從新註冊JNI函數,也就是模擬Zygote創世界中前兩天的工做,最後調用android.app.ActivityThread的main函數。這種方式應該是可行的,但難度較大,並且會影響運行速度。

關於本節所提出的問題,歡迎廣大讀者踊躍討論。

4.5.2  開機速度優化

Android開機速度慢這一現象一直受人詬病,Google好像也沒有要作這方面優化的意向,那麼,在實際工做中又在哪些地方能夠作一些優化呢?根據我目前所掌握的資料分析,有三個地方耗時比較長:

·  ZygoteInit的main函數中preloadClasses加載的那一千多個類。

·  開機啓動時,會對系統內全部的apk文件掃描並收集信息,這個動做耗費的時間很是長。

·  SystemServer建立的那些Service,會佔用很多時間。

咱們這裏討論第一個問題,如何減小preloadClasses的時間呢?其實,這個函數是能夠去掉的,由於系統最終仍是會在使用這些類時去加載,但這樣就破壞了Android採用fork機制來建立Java進程的本意,而fork機制的好處則是顯而易見的:

·  Zygote預加載的這些class,在fork子進程時,僅需作一個複製便可。這樣就節約了子進程的啓動時間。

·  根據fork的copy-on-write機制,有些類若是不作改變,甚至連複製都不用,它們會直接和父進程共享數據。這樣就會省去很多內存的佔用。

開機速度優化是一項比較複雜的研究,目前有人使用Berkeley Lab Checkpoint/Restart(BLCR)技術來提高開機速度。這一技術的構想其實挺簡單,就是對當前系統作一個快照,保存到一個文件中,當系統重啓時,直接根據文件的快照信息來恢復重啓以前的狀態。固然想法很簡單,實現倒是很複雜的,這裏,咱們對此不作進一步的討論了,讀者可自行展開深刻的思考和研究。

我在VMWare虛擬機上使用過相似的技術,它叫Snapshort。開機速度的問題我更但願Google本身能加以重視並推進它的解決。

 

4.5.3 Watchdog分析

本章咱們沒有對SystemServer作更進一步的分析,不過作爲拓展內容,這裏想介紹一下Watchdog。Watch Dog的中文意思是「看門狗」。我依稀記得,其最初存在的意義是由於早期嵌入式設備上的程序常常「跑飛」(好比說電磁干擾等),因此專門有個硬件看門狗,每隔一段時間,看門狗就去檢查一下某個參數是否是被設置了,若是發現該參數沒有被設置,則判斷爲系統出錯,而後就會強制重啓。

軟件層面上Android對SystemServer對參數是否被設置也很謹慎,專門爲它增長了一條看門狗,可它看的是哪一個門呢?對了,就是看幾個重要Service的門,一旦發現Service出了問題,就會殺掉system_server,這樣就使zygote隨其一塊兒自殺,最後致使重啓Java世界。

咱們先把SystemServe使用Watchdog的調用流程總結一下,而後以這個爲切入點來分析Watchdog。SS和Watchdog的交互流程能夠總結爲如下三個步驟:

·  Watchdog. getInstance().init()

·  Watchdog.getInstance().start()

·  Watchdog. getInstance().addMonitor()

這三個步驟都很是簡單。先看第一步:

1. 建立和初始化Watchdog

getInstance用於建立Watchdog,一塊兒來看看,代碼以下所示:

[-->Watchdog.java]

public static Watchdog getInstance() {

 if(sWatchdog == null) {

      sWatchdog= new Watchdog(); //使用了單例模式。

  }

   returnsWatchdog;

}

public class Watchdog extends Thread 

//Watchdog從線程類派生,因此它會在單獨的一個線程中執行

private Watchdog() {

       super("watchdog");

    //構造一個Handler,Handler的詳細分析見第5章,讀者能夠簡單地把它看作是消息處理的地方。

   //它在handleMessage函數中處理消息

       mHandler = new HeartbeatHandler();

      //GlobalPssCollected和內存信息有關。

      mGlobalPssCollected= new GlobalPssCollected();

    }

 

這條看門狗誕生後,再來看看init函數,代碼以下所示:

[-->Watchdog.java]

public void init(Context context, BatteryServicebattery,

           PowerManagerService power, AlarmManagerService alarm,

           ActivityManagerService activity) {

 

        mResolver = context.getContentResolver();

       mBattery = battery;

       mPower = power;

       mAlarm = alarm;

       mActivity = activity;

        ......

 

       mBootTime = System.currentTimeMillis();//獲得當前時間

        ......

}

至此,看門狗誕生的知識就介紹完了,下面咱們就讓它動起來。

2. 看門狗跑起來

SystemServer調用Watchdog的start函數,這將致使Watchdog的run在另一個線程中被執行。代碼以下所示:

[-->Watchdog.java]

public void run() {

     booleanwaitedHalf = false;

     while(true) {//外層while循環

     mCompleted= false; //false代表各個服務的檢查還沒完成。

     /*

       mHandler的消息處理是在另一個線程上,這裏將給那個線程的消息隊列發條消息

       請求Watchdog檢查Service是否工做正常。

   */

    mHandler.sendEmptyMessage(MONITOR);

   synchronized (this) {

          long timeout = TIME_TO_WAIT;

           long start = SystemClock.uptimeMillis();

           //注意這個小while循環的條件,mForceKillSystem爲true也會致使退出循環

               while (timeout > 0 && !mForceKillSystem) {

                 try {

                        wait(timeout);  //等待檢查的結果

                    } catch(InterruptedException e) {

                      }

                    timeout = TIME_TO_WAIT -(SystemClock.uptimeMillis() - start);

               }

             //mCompleted爲true,表示service一切正常

              if (mCompleted &&!mForceKillSystem) {

                   waitedHalf = false;

                    continue;

               }

                //若是mCompleted不爲true,看門狗會比較盡責,再檢查一次

               if (!waitedHalf) {

                    ......

                    waitedHalf = true;

                    continue;//再檢查一次

               }

           }

           //已經檢查過兩次了,仍是有問題,這回是真有問題了。因此SS須要把本身幹掉。

           if (!Debug.isDebuggerConnected()) {

                 Process.killProcess(Process.myPid());

                  System.exit(10); //幹掉本身

           }

           ......

           waitedHalf = false;

        }

}

OK,這個run函數仍是比較簡單的,就是:

·  隔一段時間給另一個線程發送一條MONITOR消息,那個線程將檢查各個Service的健康狀況。而看門狗會等待檢查結果,若是第二次尚未返回結果,那麼它會殺掉SS。

好吧,來看看檢查線程到底是怎麼檢查Service的。

3. 列隊檢查

這麼多Service,哪些是看門狗比較關注的呢?一共有三個Service是須要交給Watchdog檢查的:

·  ActivityManagerService

·  PowerManagerService

·  WindowManagerService

要想支持看門狗的檢查,就須要這些Service實現monitor接口,而後Watchdog就會調用它們的monitor函數進行檢查了。檢查的地方是在HeartbeatHandler類的handleMessage中,代碼以下所示:

[-->Watchdog.java::HeartbeatHandler]

final class HeartbeatHandler extends Handler {

       @Override

       public void handleMessage(Message msg) {

           switch (msg.what) {

               ......

               case MONITOR: {

                    ......

                   long now =SystemClock.uptimeMillis();

                  final int size =mMonitors.size();

                   //檢查各個服務,並設置當前檢查的對象爲mCurrentMonitor

                    for (int i = 0 ; i <size ; i++) {

                        mCurrentMonitor =mMonitors.get(i);

                       mCurrentMonitor.monitor();//檢查這個對象

                    }

                    //若是沒問題,則設置mCompleted爲真。

                    synchronized (Watchdog.this){

                        mCompleted = true;

                        mCurrentMonitor = null;

                    }

               } break;

           }

        }

}

那麼,Service的健康是怎麼判斷的呢?咱們以PowerManagerService爲例,先看看它是怎麼把本身交給看門狗檢查的。

[-->PowerManagerService.java]

PowerManagerService()

{

   ......

   //在構造函數中把本身加入Watchdog的檢查隊列

   Watchdog.getInstance().addMonitor(this);

}

而Watchdog調用各個monitor函數到底檢查了些什麼呢?再看看它實現的monitor函數吧。

[-->PowerManagerService.java]

public void monitor() {

 //monitor原來檢查的就是這些Service是否是發生死鎖了!

   synchronized (mLocks) { }

}

原來,Watchdog最怕系統服務死鎖了,對於這種狀況也只能採起殺系統的辦法了。

這種狀況,我只碰到過一次,緣由是有一個函數佔着鎖,但長時間沒有返回。沒返回的緣由是這個函數須要和硬件交互,而硬件又沒有及時返回。

關於Watchdog,咱們就介紹到這裏。另外,它還能檢查內存的使用狀況,這一部份內容讀者能夠自行研究。

4.6  本章小結

本章對Zygote進程作了較爲深刻的分析,Zygote的主要工做是開創Java世界,本章介紹了它創世紀的七大步驟。另外,本章還分析了Zygote的「嫡長子」——System_server進程,這個進程是Java世界中的系統Service的駐留地,因此它很是重要。對於System_server進程,本章重點關注的是它的建立和初始化過程。此外,咱們還分析了一個Activity所屬進程的建立過程,原來這個進程是由ActivityManagerService發送請求給Zygote,最後由Zygote經過fork的方式建立的。

在本章拓展部分,咱們討論了Dalvik虛擬機對heap大小的設置及其可能的修改方法,另外還探討了Android系統開機速度的問題。最後,本章還分析了System_server中Watchdog的工做流程。

相關文章
相關標籤/搜索