Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android啟動過程詳解(3)——Zygote

Android啟動過程詳解(3)——Zygote

編輯:關於Android編程

由於Android系統是基於Linux的,所以在Android系統存在兩個不一樣的空間,Android空間(Java空間)以及Native空間。系統啟動的時候當然是Native空間,所以必須有一個進程來打開Android空間。同時,盡管Android的設計者在淡化進程的概念,強化Activity,Service, BroadcastReceiver等等組件的概念,但從Linux的視角來看,每一個應用都是寄生在一個進程上的,那麼創建進程也同樣需要從Native空間去創建。在Android世界中Zygote就擔任了這個角色,所以所有應用程序進程的父進程都是Zygote。Zygote的意思是受精卵,所以從名字上就能看出來它的作用。今天就來討論一下Zygote的作用。

1.Zygote的啟動

在上一篇博客介紹init進程啟動的時候講到了init.rc文件的解析,其中就有關於Zygote的啟動:

#zygote service
service zygote /system/bin/app_process -Xzygote/system/bin –zygote \
 --start-system-server 
   socketzygote stream 666  #socket關鍵字表示OPTION
   onrestart write /sys/android_power/request_state wake #onrestart也是OPTION
   onrestart write /sys/power/state on
   onrestart restart media

從中可以看到zygote的啟動是通過/system/bin/app_process進程啟動的。所以zygote最初的名字叫“app_process”,這個名字是在Android.mk文件中被指定的,但app_process在運行過程中,通過Linux下的pctrl系統調用將自己的名字換成了“zygote”,所以我們通過ps命令看到的進程名是“zygote”。接下來看看app_process的啟動:

<code><code><code>[-->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; 0="" marglen="" appruntime="" int="" i="runtime.addVmArguments(argc," if="" system="" bin="" runtime.mparentdir="argv[i++];" arg="argv[i++];" true="" bool="" startsystemserver="(i" :="" ......="" pre="">

在這個函數中一共做了兩件重要的事: - 創建Android運行時環境,AppRuntime - 啟動SystemServer SystemServer是管理Android所有服務的部件,這一部分放到下一篇博客講,重點來講一下AppRumtime的創建。 這裡寫圖片描述 從上面這幅圖可以看到,AppRuntime繼承自AndroidRumtime,所以在app_process進程中調用的runtime.start其實是調用的AndroidRumtime的start函數:

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;
    }

   jclass stringClass;
   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);
   jclass startClass;
   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);
}

上面這段代碼主要做了三件事: - startVM:注冊Java虛擬機,並設置虛擬機相關參數 - startReg:注冊JNI函數,因為後續Java世界用到的一些函數是采用native方式來實現的,所以才必須提前注冊這些函數。 - 調用ZygoteInit.main正式進入Java世界

接下來重點講一下第三點。首先來看看源碼:

[-->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;
        }
     ......
    }

在這個函數中主要做了四件事: - 創建IPC通信Socket - 預加載類和資源 - 啟動SystemServer - 監聽其他進程發出來的請求

1.1 創建IPC通信接口Socket——registerZygoteSocket

Zygote和其他進程之間的IPC並不是通過binder機制,而是通過一個localsocket來完成的,所以要首先創建一個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));
       }
       }

}

1.2 預加載類和資源

這部分就不細講了,邏輯比較簡單,就是提前加載好一些運行時經常會用到的類和資源,提升系統運行的效率,但這同時會導致系統在啟動的時候時間過長,所以這是一個取捨的問題。

1.3 啟動SystemServer——startSystemServer

這是Zygote非常重要的一個功能,因為Android啟動的所有Service(ActivityManagerService,WindowManagerService,PackageManagerService)都是通過SystemServer來啟動的。

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);
        }

        if(pid == 0) {
         //① system_server進程的工作
           handleSystemServerProcess(parsedArgs);
        }

       //zygote返回true
       return true;
    }

邏輯還是比較簡單的,就是通過fork創建出SystemServer進程,具體SystemServer啟動如何工作下一篇博客會詳細介紹。

1.4 監聽其他進程發出來的請求——runSelectLoopMode

registerZygoteSocket中注冊了一個用於IPC的Socket,不過那時還沒有地方用到它。它的用途將在這個runSelectLoopMode中體現出來,請看下面的代碼:

[-->ZygoteInit.java]

private static void runSelectLoopMode()
throws MethodAndArgsCaller {
       ArrayList fds = new ArrayList();
       ArrayList 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來處理。

2.處理請求

Zygote在啟動過程中的最後一步是進入runSelectLoopMode來監聽socket,接收請求。比如當ActivityManagerService在接收到一個打開Activity的請求,並發現該Activity所屬的應用並沒有啟動時就會創建一個進程,具體的過程可以參考我的另一篇博客:Android應用框架之應用啟動過程

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved