天天看點

Android zygote 程序啟動分析Android zygote 程序啟動分析前言概述zygote 程序啟動zygote 程序入口ZygoteInit.main整體流程圖參考

Android zygote 程序啟動分析

文章目錄

  • Android zygote 程序啟動分析
  • 前言
  • 概述
  • zygote 程序啟動
  • zygote 程序入口
    • AppRuntim.start
    • AppRuntime.startVm
    • AppRuntime.startReg
  • ZygoteInit.main
    • ZygoteInit.registerZygoteSocket
    • ZygoteInit.preload
    • ZygoteInit.startSystemServer
    • ZygoteInit.runSelectLoop
  • 整體流程圖
  • 參考

前言

zygote 程序是 Android 系統中第一個擁有 Java 運作環境的程序,它由使用者空間 1 号程序 init 程序通過解析 init.rc 檔案建立,從 init 程序 fork 而來。從 zygote(受精卵)這個富含生物意義的名字可以知道,它是一個孵化器。Android 系統中所有運作在 Java 虛拟機中的系統服務以及應用均由 zygote 程序孵化而來。

了解 zygote 程序的啟動過程以及所做的工作,将為了解 Java 層系統服務以及所有應用的程序啟動流程打下基礎。

概述

zygote 通過克隆(fork)的方式建立子程序,fork 出來的子程序将繼承父程序的所有資源,基于這個特性,zygote 程序在啟動過程将建立 Java ART 虛拟機,預加載一個 Java 程序需要的所有系統資源,之後子程序被建立後,就可以直接使用這些資源運作了。

自 Android 5.0 系統開始,zygote 不再是一個程序,而是兩個程序,一個是 32 位 zygote,負責孵化 32 位程序(為了相容使用了 armeabi 和 armeabi-v7a 等 32 位架構的本地動态庫的應用),另一個是 64 位 zygote 程序,負責孵化 64 位應用程序(可加載 arm64-v8a 等 64 位架構本地庫)。

zygote 程序主要做了如下工作:

  1. 建立虛拟機,加載系統 Java 類以及注冊系統類所依賴的 JNI 方法;
  2. 預加載應用程式程序所需的 drawable 和 color 資源,準備 WebView 和 OpenGL;
  3. 建立 socket 服務端,以便接收和處理建立應用程序的請求;
  4. 啟動 system_server 服務程序,用于承載整個 framework 層運作的系統服務;
  5. 待命以随即處理到來的任務請求。

參考相關資料,對 Android 6.0.1 系統中 zygote 程序啟動關鍵流程進行分析。

zygote 程序啟動

zygote 程序由 init 解析 init.rc 檔案啟動,首先看一下啟動 zygote 的 rc 檔案内容:

32 位 zygote 啟動内容在 init.zygote32.rc 檔案中,64 位 zygote 啟動内容在 init.zygote64.rc 中:

提示:自 Android 9.0 系統開始,兩個 zygote 啟動配置放在一個檔案中:init.zygote64_32.rc。

# init.zygote32.rc

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 media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks
           
# init.zygote64.rc

service zygote /system/bin/app_process64 -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 media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks
           

兩者的唯一差別隻在于可執行檔案的不同,

/system/bin/app_process

/system/bin/app_process64

zygote 将在如下情況下重新開機:

  1. servicemanager 程序死亡(啟動配置中包含

    onrestart restart zygote

    );
  2. surfaceflinger 程序死亡(啟動配置中包含

    onrestart restart zygote

    );
  3. Zygote 死亡(啟動配置中為非 oneshot);
  4. system_server 程序死亡;

zygote 程序入口

zygote 可執行檔案

app_process

的實作代碼在

frameworks/base/cmds/app_process/app_main.cpp

中,入口為

main

函數:

// app_main.cpp

// ...
#if defined(__LP64__)
// 如果為 64 位程序,則程序名為 "zygote64",否則為 "zygote"
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
static const char ZYGOTE_NICE_NAME[] = "zygote64";
#else
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
static const char ZYGOTE_NICE_NAME[] = "zygote";
#endif

int main(int argc, char* const argv[])
{
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
    // 舊版核心不識别 PR_SET_NO_NEW_PRIVS,将傳回 EINVAL,避免在舊版核心上死掉
    if (errno != EINVAL) {
      LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
      return 12;
    }
  }

  // argv[0] = "/system/bin/app_process"
  AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
  // 跳過 argv[0] 參數
  argc--;
  argv++;

  // 直到遇到 '-' 或第一個非 '-' 的參數為止的所有内容都将提供給虛拟機作為 options。
  //
  // --zygote:             啟動到 zygote 模式
  // --start-system-server:啟動 system server
  // --application:	    以應用程式模式啟動 (獨立啟動, 非 zygote)
  // --nice-name:	    給程序起一個好名字
  //
  // 對于非 zygote 啟動,這些參數後面将是主類名,所有其餘的參數都傳遞給此類的 main 方法;
  // 對于 zygote 啟動,所有剩餘的參數都傳遞給 zygote 的 main 方法。

  int i;
  for (i = 0; i < argc; i++) {
    if (argv[i][0] != '-') {
      break;
    }
    if (argv[i][1] == '-' && argv[i][2] == 0) {
      ++i;
      break;
    }
    runtime.addOption(strdup(argv[i]));
  }

  bool zygote = false;
  bool startSystemServer = false;
  bool application = false;
  String8 niceName;
  String8 className;

  ++i;
  while (i < argc) {
    const char* arg = argv[i++];
    if (strcmp(arg, "--zygote") == 0) {
      zygote = true;
      niceName = ZYGOTE_NICE_NAME;
    } else if (strcmp(arg, "--start-system-server") == 0) {
      startSystemServer = true;
    } else if (strcmp(arg, "--application") == 0) {
      application = true;
    } else if (strncmp(arg, "--nice-name=", 12) == 0) {
      niceName.setTo(arg + 12);
    } else if (strncmp(arg, "--", 2) != 0) {
      className.setTo(arg);
      break;
    } else {
      --i;
      break;
    }
  }

  Vector<String8> args;
  if (!className.isEmpty()) {
    // 沒有處于 zygote 模式
    args.add(application ? String8("application") : String8("tool"));
    runtime.setClassNameAndArgs(className, argc - i, argv + i);
  } else {
    // className 為空,處于 zygote 模式

    // 建立 /data/dalvik-cache/ 目錄
    maybeCreateDalvikCache();

    if (startSystemServer) {
      args.add(String8("start-system-server"));
    }

    char prop[PROP_VALUE_MAX];
    if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
      LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
        ABI_LIST_PROPERTY);
      return 11;
    }

    String8 abiFlag("--abi-list=");
    abiFlag.append(prop);
    // 擷取支援的 abi 清單
    args.add(abiFlag);

    // 在 zygote 模式下,将所有剩餘參數傳遞給 zygote 的 main() 方法。
    for (; i < argc; ++i) {
      args.add(String8(argv[i]));
    }
  }

  if (!niceName.isEmpty()) {
    // 設定程序名字
    runtime.setArgv0(niceName.string());
    set_process_name(niceName.string());
  }

  if (zygote) {
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
  } else if (className) {
    runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
  } else {
    fprintf(stderr, "Error: no class name or --zygote supplied.\n");
    app_usage();
    LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    return 10;
  }
}
           

AppRuntime.main

函數做了如下工作:

  1. 建立了

    AppRuntime

    對象,傳入虛拟機所需的選項;
  2. 解析 init.rc 檔案的

    zygote

    啟動參數;
  3. 調用

    AppRuntime.start

    函數,根據啟動 zygote 還是指令行(

    className

    ),進入

    ZygoteInit

    或者

    RuntimeInit

    參數分支。

提示:

app_process

可使用指令行調用,啟動一個 Java 類,并調用

main

方法,此時有

className

參數,處于非 zygote 模式。

格式:

app_process [可選參數] 指令所在路徑 啟動的類名 [可選參數]
           

示例:

app_process -Djava.class.path=Hello.dex /data/local/ com.example.Hello
           

AppRuntim.start

下面進入

AppRuntime

start

函數中,以

ZygoteInit

參數分支為路徑進行分析:

// AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
  ALOGD(">>>>>> START %s uid %d <<<<<<\n",
          className != NULL ? className : "(unknown)", getuid());

  static const String8 startSystemServer("start-system-server");

  for (size_t i = 0; i < options.size(); ++i) {
    if (options[i] == startSystemServer) {
      const int LOG_BOOT_PROGRESS_START = 3000;
      LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
    }
  }

  const char* rootDir = getenv("ANDROID_ROOT");
  if (rootDir == NULL) {
    rootDir = "/system";
    if (!hasDir("/system")) {
      LOG_FATAL("No root directory specified, and /android does not exist.");
        return;
     }
     setenv("ANDROID_ROOT", rootDir, 1);
  }

  JniInvocation jni_invocation;
  jni_invocation.Init(NULL);
  JNIEnv* env;
  // 建立虛拟機
  if (startVm(&mJavaVM, &env, zygote) != 0) {
    return;
  }
  onVmCreated(env);
  // 注冊系統類 JNI 方法
  if (startReg(env) < 0) {
    ALOGE("Unable to register all android natives\n");
    return;
  }
  
  jclass stringClass;
  jobjectArray strArray;
  jstring classNameStr;
  
  stringClass = env->FindClass("java/lang/String");
  assert(stringClass != NULL);
  // Java: strArray = new String[options.size() + 1];
  strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
  assert(strArray != NULL);
  classNameStr = env->NewStringUTF(className);
  assert(classNameStr != NULL);
  // Java: strArray[0] = "com.android.internal.os.ZygoteInit";
  env->SetObjectArrayElement(strArray, 0, classNameStr);
  
  // 将相關參數收集至 options 中,下面會傳遞給 ZygoteInit
  // --start-system-server, --abi-list=arm64-v8a ...
  for (size_t i = 0; i < options.size(); ++i) {
    jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
    assert(optionsStr != NULL);
    env->SetObjectArrayElement(strArray, i + 1, optionsStr);
  }
  
  // 轉換為 JNI 格式類名:com/android/internal/os/ZygoteInit
  char* slashClassName = toSlashClassName(className);
  jclass startClass = env->FindClass(slashClassName);
  if (startClass == NULL) {
    ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
  } else {
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
       "([Ljava/lang/String;)V");
    if (startMeth == NULL) {
      ALOGE("JavaVM unable to find main() in '%s'\n", className);
    } else {
      // 調用 ZygoteInit.main();
      env->CallStaticVoidMethod(startClass, startMeth, strArray);
    }
  }
  free(slashClassName);
  
  ALOGD("Shutting down VM\n");
  // 關閉 Java 虛拟機
  if (mJavaVM->DetachCurrentThread() != JNI_OK)
    ALOGW("Warning: unable to detach main thread\n");
  if (mJavaVM->DestroyJavaVM() != 0)
    ALOGW("Warning: VM did not shut down cleanly\n");
}
           

上面的代碼也比較清晰,工作如下:

  1. startVm

    函數建立 ART 虛拟機;
  2. startReg

    函數注冊系統 JNI 方法;
  3. 收集

    options

    參數,調用并傳遞給

    ZygoteInit.main()

下面針對每個方法具體分析。

AppRuntime.startVm

// AndroidRuntime.cpp

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
  JavaVMInitArgs initArgs;

  // ...

  bool checkJni = false;
  property_get("dalvik.vm.checkjni", propBuf, "");
  if (strcmp(propBuf, "true") == 0) {
    checkJni = true;
  } else if (strcmp(propBuf, "false") != 0) {
    property_get("ro.kernel.android.checkjni", propBuf, "");
    if (propBuf[0] == '1') {
      checkJni = true;
    }
  }
  ALOGD("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
  if (checkJni) {
    /* 擴充的 JNI 檢查 */
    addOption("-Xcheck:jni");

    /* 啟用 -Xcheck:jni,它提供了 JNI 函數調用跟蹤 */
    //addOption("-verbose:jni");
  }

  parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf,
                     "-Xzygote-max-boot-retry=");

  // ...
 
  property_get("dalvik.vm.execution-mode", propBuf, "");
  if (strcmp(propBuf, "int:portable") == 0) {
    executionMode = kEMIntPortable;
  } else if (strcmp(propBuf, "int:fast") == 0) {
    executionMode = kEMIntFast;
  } else if (strcmp(propBuf, "int:jit") == 0) {
    executionMode = kEMJitCompiler;
  }

  // ...

  // 預設的起始和最大堆大小,通常根據具體廠商硬體進行合适的配置
  parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");
  parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");
  
  parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");
  parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree=");
  parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");
  parseRuntimeOption("dalvik.vm.heaptargetutilization", heaptargetutilizationOptsBuf, "-XX:HeapTargetUtilization=");

  // ...

  // 確定有可預加載的類檔案,preloaded-classes 中存放了所有需要被預加載的系統 Java 類
  if (!hasFile("/system/etc/preloaded-classes")) {
    ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n", strerror(errno));
    return -1;
  }

  // ... 省略數百行 addOption 相關語句 

  // 解析 fingerprint 資訊并提供給運作時,ANR dump 資訊将包含 fingerprint 資訊且能夠解析
  parseRuntimeOption("ro.build.fingerprint", fingerprintBuf, "-Xfingerprint:");

  initArgs.version = JNI_VERSION_1_4;
  initArgs.options = mOptions.editArray();
  initArgs.nOptions = mOptions.size();
  initArgs.ignoreUnrecognized = JNI_FALSE;

  // 初始化虛拟機。JavaVM* 是基于程序的,而 JNIEnv* 是基于線程的,
  // 如果調用成功,表示虛拟機已就緒,可以開始 JNI 調用了。
  if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
    ALOGE("JNI_CreateJavaVM failed\n");
    return -1;
  }

  return 0;
}
           

上述代碼主要是為虛拟機初始化添加運作選項,最後調用

JNI_CreateJavaVM

建立虛拟機,下面就進入了虛拟機流程了,需要了解虛拟機時再探究其源碼。

下面回到上面

AppRuntime.start

函數,看

startReg

的實作。

AppRuntime.startReg

// AndroidRuntime.cpp

#define NELEM(x) (sizeof(x)/sizeof(*(x))) 

/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
  // 設定 Thread 建立函數,用于支援 Android Native 層線程封裝
  androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

  ALOGV("--- registering native functions ---\n");

  env->PushLocalFrame(200);

  // 注冊 JNI 方法
  if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
    env->PopLocalFrame(NULL);
    return -1;
  }
  env->PopLocalFrame(NULL);

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

  return 0;
}
           
// AndroidRuntim.cpp

#define REG_JNI(name)      { name, #name }
struct RegJNIRec {
    int (*mProc)(JNIEnv*);
    const char* mName;
};

static const RegJNIRec gRegJNI[] = {
  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),
  REG_JNI(register_android_content_AssetManager),
  REG_JNI(register_android_content_StringBlock),
  // ... 省略數百行
  REG_JNI(register_android_animation_PropertyValuesHolder),
  REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
  REG_JNI(register_com_android_internal_net_NetworkStatsFactory),
};

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
  for (size_t i = 0; i < count; i++) {
    if (array[i].mProc(env) < 0) {
#ifndef NDEBUG
      ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
      return -1;
    }
  }
  return 0;
           

startReg

函數主要是注冊系統類的 JNI 方法,使用

register_jni_procs

進行循環調用注冊方法,每個

mProc

都指向

gRegJNI

數組裡存放的結構體中的

mProc

函數指針,例如

register_com_android_internal_os_RuntimeInit

,檢視一下實作:

static JNINativeMethod gMethods[] = {
  { "nativeFinishInit", "()V",
    (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
  { "nativeZygoteInit", "()V",
    (void*) com_android_internal_os_RuntimeInit_nativeZygoteInit },
  { "nativeSetExitWithoutCleanup", "(Z)V",
    (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
};

int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
  return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
      gMethods, NELEM(gMethods));
}
           

startReg

之後下面就是通過

env->CallStaticVoidMethod

啟動

Zygoteinit.main

進入 Java 層了。

ZygoteInit.main

// ZygoteInit.java

public static void main(String argv[]) {
  try {
    RuntimeInit.enableDdms();
    SamplingProfilerIntegration.start();

    boolean startSystemServer = false;
    String socketName = "zygote";
    String abiList = null;
    for (int i = 1; i < argv.length; i++) {
      if ("start-system-server".equals(argv[i])) {
        startSystemServer = 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.");
    }

    // 注冊 socket 服務端
    registerZygoteSocket(socketName);
    EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
        SystemClock.uptimeMillis());
    // 預加載資源
    preload();
    EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
        SystemClock.uptimeMillis());

    SamplingProfilerIntegration.writeZygoteSnapshot();

    // 在啟動後執行初始 gc
    gcAndFinalize();

    Trace.setTracingEnabled(false);

    if (startSystemServer) {
      // 啟動 system_server
      startSystemServer(abiList, socketName);
    }

    Log.i(TAG, "Accepting command socket connections");
    // 啟動 socket 循環
    runSelectLoop(abiList);
    closeServerSocket();
  } catch (MethodAndArgsCaller caller) {
    caller.run();
  } catch (RuntimeException ex) {
    Log.e(TAG, "Zygote died with exception", ex);
    closeServerSocket();
    throw ex;
  }
}
           

上述代碼做了如下工作:

  1. 使用

    registerZygoteSocket

    注冊 socket 服務端,為能夠接收建立子程序的請求提供支援;
  2. 使用

    preload()

    預加載應用程序所需資源;
  3. 啟動

    system_server

    服務程序;
  4. 啟動 socket 循環,等待外部請求,随時響應處理。

下面進入函數具體分析。

ZygoteInit.registerZygoteSocket

// ZygoteInit.java

private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";

// ...

private static void registerZygoteSocket(String socketName) {
  if (sServerSocket == null) {
    int fileDesc;
    final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
    try {
      String env = System.getenv(fullSocketName);
      fileDesc = Integer.parseInt(env);
    } catch (RuntimeException ex) {
      throw new RuntimeException(fullSocketName + " unset or invalid", ex);
    }

    try {
      FileDescriptor fd = new FileDescriptor();
      fd.setInt$(fileDesc);
      // 建立了本地 socket 服務端
      sServerSocket = new LocalServerSocket(fd);
    } catch (IOException ex) {
      throw new RuntimeException(
            "Error binding to local socket '" + fileDesc + "'", ex);
    }
  }
}
           

主是要建立了本地服務端 socket 對象,命名為

ANDROID_SOCKET_zygote

ZygoteInit.preload

// ZygoteInit.java

static void preload() {
  Log.d(TAG, "begin preload");
  // 預加載 /system/etc/preloaded-classes 檔案中的系統類
  preloadClasses();
  // 預加載 drawable 和 color 資源
  preloadResources();
  // 預加載 OpenGL
  preloadOpenGL();
  // 預加載共享庫
  preloadSharedLibraries();
  // 預加載文本資源
  preloadTextResources();
  // WebView 相關初始化
  WebViewFactory.prepareWebViewInZygote();
  Log.d(TAG, "end preload");
}

private static void preloadSharedLibraries() {
  Log.i(TAG, "Preloading shared libraries...");
  System.loadLibrary("android");
  System.loadLibrary("compiler_rt");
  System.loadLibrary("jnigraphics");
}

private static void preloadOpenGL() {
  if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
    EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
  }
}

private static void preloadTextResources() {
  Hyphenator.init();
}

private static void preloadClasses() {
  final VMRuntime runtime = VMRuntime.getRuntime();
  InputStream is;
  try {
    is = new FileInputStream(PRELOADED_CLASSES);
  } catch (FileNotFoundException e) {
    Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
    return;
  }

  // ...
   
  try {
    BufferedReader br = new BufferedReader(new InputStreamReader(is), 256);
    int count = 0;
    String line;
    while ((line = br.readLine()) != null) {
      line = line.trim();
      if (line.startsWith("#") || line.equals("")) {
        continue;
      }
      try {
	// 預加載類
        Class.forName(line, true, null);
        count++;
      } catch (ClassNotFoundException e) {
        Log.w(TAG, "Class not found for preloading: " + line);
      } catch (UnsatisfiedLinkError e) {
        Log.w(TAG, "Problem preloading " + line + ": " + e);
      } catch (Throwable t) {
        // ...
        throw new RuntimeException(t);
      }
    }
    Log.i(TAG, "...preloaded " + count + " classes in "
          + (SystemClock.uptimeMillis()-startTime) + "ms.");
  } catch (IOException e) {
    Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
  } finally {
    IoUtils.closeQuietly(is);
    runtime.setTargetHeapUtilization(defaultUtilization);
    // 使用已加載類填充 DexCaches
    runtime.preloadDexCaches();
    // ...
  }
}

private static void preloadResources() {
  final VMRuntime runtime = VMRuntime.getRuntime();
  try {
    mResources = Resources.getSystem();
    mResources.startPreloading();
    if (PRELOAD_RESOURCES) {
      Log.i(TAG, "Preloading resources...");
      long startTime = SystemClock.uptimeMillis();
      TypedArray ar = mResources.obtainTypedArray(
              com.android.internal.R.array.preloaded_drawables);
      int N = preloadDrawables(runtime, ar);
      ar.recycle();
      Log.i(TAG, "...preloaded " + N + " resources in "
              + (SystemClock.uptimeMillis()-startTime) + "ms.");
      startTime = SystemClock.uptimeMillis();
      ar = mResources.obtainTypedArray(
              com.android.internal.R.array.preloaded_color_state_lists);
      N = preloadColorStateLists(runtime, ar);
      ar.recycle();
      Log.i(TAG, "...preloaded " + N + " resources in "
              + (SystemClock.uptimeMillis()-startTime) + "ms.");
    }
    mResources.finishPreloading();
  } catch (RuntimeException e) {
    Log.w(TAG, "Failure preloading resources", e);
  }
}

private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
  int N = ar.length();
  for (int i=0; i<N; i++) {
    int id = ar.getResourceId(i, 0);
    if (id != 0) {
      if (mResources.getColorStateList(id, null) == null) {
        throw new IllegalArgumentException(
                "Unable to find preloaded color resource #0x"
                + Integer.toHexString(id)
                + " (" + ar.getString(i) + ")");
      }
    }
  }
  return N;
}

private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
  int N = ar.length();
  for (int i=0; i<N; i++) {
    int id = ar.getResourceId(i, 0);
    if (id != 0) {
      if (mResources.getDrawable(id, null) == null) {
        throw new IllegalArgumentException(
                "Unable to find preloaded drawable resource #0x"
                + Integer.toHexString(id)
                + " (" + ar.getString(i) + ")");
      }
    }
  }
  return N;
}
           

上面幾乎把加載資源的所有代碼都列了出來。

預加載這些資源的目的是為了将資源提前放置在記憶體中,當建立子程序的時候,可以直接使用這些資源,而不必每次都重新加載一遍。

ZygoteInit.startSystemServer

// ZygoteInit.java

private static boolean startSystemServer(String abiList, String socketName)
        throws MethodAndArgsCaller, RuntimeException {
  long capabilities = posixCapabilitiesAsBits(
    OsConstants.CAP_BLOCK_SUSPEND,
    OsConstants.CAP_KILL,
    OsConstants.CAP_NET_ADMIN,
    OsConstants.CAP_NET_BIND_SERVICE,
    OsConstants.CAP_NET_BROADCAST,
    OsConstants.CAP_NET_RAW,
    OsConstants.CAP_SYS_MODULE,
    OsConstants.CAP_SYS_NICE,
    OsConstants.CAP_SYS_RESOURCE,
    OsConstants.CAP_SYS_TIME,
    OsConstants.CAP_SYS_TTY_CONFIG
  );

  String args[] = {
    "--setuid=1000",
    "--setgid=1000",
    "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
    "--capabilities=" + capabilities + "," + capabilities,
    "--nice-name=system_server",
    "--runtime-args",
    "com.android.server.SystemServer",
  };
  ZygoteConnection.Arguments parsedArgs = null;
  
  int pid;
  
  try {
    parsedArgs = new ZygoteConnection.Arguments(args);
    ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
    ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
  
    // fork 出 system_server 程序并使用相關參數設定程序資訊
    pid = Zygote.forkSystemServer(
        parsedArgs.uid, parsedArgs.gid,
        parsedArgs.gids,
        parsedArgs.debugFlags,
        null,
        parsedArgs.permittedCapabilities,
        parsedArgs.effectiveCapabilities);
  } catch (IllegalArgumentException ex) {
    throw new RuntimeException(ex);
  }
  
  if (pid == 0) {
    // 進入子程序
    if (hasSecondZygote(abiList)) {
      // 等待另一個架構的 zygote 程序啟動
      waitForSecondaryZygote(socketName);
    }
  
    // 處理 system_server 剩餘工作
    handleSystemServerProcess(parsedArgs);
  }
  
  return true;
}
           

上面的代碼使用

Zygote.forkSystemServer

fork 出了子程序,然後将相關參數設定給子程序,例如其中的

uid=1000

gid=1000

,下面就進入

handleSystemServerProcess

方法,開始

system_server

的工作。

ZygoteInit.runSelectLoop

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
  ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
  ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

  fds.add(sServerSocket.getFileDescriptor());
  peers.add(null);
  while (true) {
    StructPollfd[] pollFds = new StructPollfd[fds.size()];
    for (int i = 0; i < pollFds.length; ++i) {
      pollFds[i] = new StructPollfd();
      pollFds[i].fd = fds.get(i);
      pollFds[i].events = (short) POLLIN;
    }
    try {
      // 監聽 socket 連接配接,阻塞并等待描述符上的可讀事件發生
      Os.poll(pollFds, -1);
    } catch (ErrnoException ex) {
      throw new RuntimeException("poll failed", ex);
    }
    for (int i = pollFds.length - 1; i >= 0; --i) {
      if ((pollFds[i].revents & POLLIN) == 0) {
        continue;
      }
      if (i == 0) {
        // i = 0 表明還未添加收到的 socket 請求對象(sServerSocket.accept())

        // 建立連接配接對象:new ZygoteConnection(sServerSocket.accept(), abiList);
        ZygoteConnection newPeer = acceptCommandPeer(abiList);
        peers.add(newPeer);
        // 添加到 fds 清單中待處理(下次循環将進行 Os.poll 等待描述符可讀)
        fds.add(newPeer.getFileDesciptor());
      } else {
        // i > 0 表示 socket 請求經過 Os.poll 表示已經可讀,那麼對請求進行處理
        boolean done = peers.get(i).runOnce();
        if (done) {
          // 處理完則移除
          peers.remove(i);
          fds.remove(i);
        }
      }
    }
  }
}
           

runSelectLoop

進入了無限循環,等待并對服務端 socket 接收到的請求進行處理,使用

runOnce

進行請求處理:

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

  String args[];
  Arguments parsedArgs = null;
  FileDescriptor[] descriptors;

  try {
    // 解析 socket 用戶端發送的指令清單
    args = readArgumentList();
    descriptors = mSocket.getAncillaryFileDescriptors();
  } catch (IOException ex) {
    Log.w(TAG, "IOException on command socket " + ex.getMessage());
    closeSocket();
    return true;
  }

  if (args == null) {
    closeSocket();
    return true;
  }

  // ...

  int pid = -1;
  FileDescriptor childPipeFd = null;
  FileDescriptor serverPipeFd = null;

  try {
    // 建立參數封裝對象
    parsedArgs = new Arguments(args);

    if (parsedArgs.abiListQuery) {
      return handleAbiListQuery();
    }
   
    // ...

    int [] fdsToClose = { -1, -1 };

    FileDescriptor fd = mSocket.getFileDescriptor();

    if (fd != null) {
      fdsToClose[0] = fd.getInt$();
    }

    fd = ZygoteInit.getServerSocketFileDescriptor();

    if (fd != null) {
      fdsToClose[1] = fd.getInt$();
    }

    fd = null;

    // 建立子程序
    pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
            parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
            parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
            parsedArgs.appDataDir);
  } catch (ErrnoException ex) {
    logAndPrintError(newStderr, "Exception creating pipe", ex);
  } catch (IllegalArgumentException ex) {
    logAndPrintError(newStderr, "Invalid zygote arguments", ex);
  } catch (ZygoteSecurityException ex) {
    logAndPrintError(newStderr,
            "Zygote security policy prevents request: ", ex);
  }

  try {
    if (pid == 0) {
      // 進入子程序
      IoUtils.closeQuietly(serverPipeFd);
      serverPipeFd = null;
      // 處理子程序邏輯
      handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
      return true;
    } else {
      IoUtils.closeQuietly(childPipeFd);
      childPipeFd = null;
      // 父程序剩餘工作
      return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
    }
  } finally {
    IoUtils.closeQuietly(childPipeFd);
    IoUtils.closeQuietly(serverPipeFd);
  }
}
           

handleChildProc

處理子程序:

private void handleChildProc(Arguments parsedArgs,
        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws ZygoteInit.MethodAndArgsCaller {
  closeSocket();
  // 關閉複制出來的服務端 socket
  ZygoteInit.closeServerSocket();

  if (descriptors != null) {
    try {
      Os.dup2(descriptors[0], STDIN_FILENO);
      Os.dup2(descriptors[1], STDOUT_FILENO);
      Os.dup2(descriptors[2], STDERR_FILENO);

      for (FileDescriptor fd: descriptors) {
          IoUtils.closeQuietly(fd);
      }
      newStderr = System.err;
    } catch (ErrnoException ex) {
      Log.e(TAG, "Error reopening stdio", ex);
    }
  }

  if (parsedArgs.niceName != null) {
    // 設定程序名字
    Process.setArgV0(parsedArgs.niceName);
  }

  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  if (parsedArgs.invokeWith != null) {
    WrapperInit.execApplication(parsedArgs.invokeWith,
            parsedArgs.niceName, parsedArgs.targetSdkVersion,
            VMRuntime.getCurrentInstructionSet(),
            pipeFd, parsedArgs.remainingArgs);
  } else {
    // 會輾轉調用到 ActivityThread.main 進入應用程式主流程
    // 内部會抛出一個 ZygoteInit.MethodAndArgsCaller 異常(為了清空調用棧),
    // 進而傳回到上面 ZygoteInit 的 main 方法的 catch 語句塊中執行
    RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
            parsedArgs.remainingArgs, null /* classLoader */);
  }
}
           

當子程序 fork 出來後,處理子程序,關閉 socket 資源,之後進入應用程式程序主流程。

整體流程圖

使用流程圖表示 zygote 整個工作過程:

Android zygote 程式啟動分析Android zygote 程式啟動分析前言概述zygote 程式啟動zygote 程式入口ZygoteInit.main整體流程圖參考

參考

  • http://gityuan.com/2016/02/13/android-zygote/