圖解 Android 系列(二)深入理解 init 與 zygote 程式

jeanboy發表於2019-04-18

介紹

這是一個連載的系列「圖解 Android 系列」,我將持續為大家提供儘可能通俗易懂的 Android 原始碼分析。

所有引用的原始碼片段,我都會在第一行標明原始檔完整路徑。為了文章篇幅考慮原始碼中間可能有刪減,刪減部分會用省略號代替。

本系列原始碼基於:Android Oreo(8.0)

init 程式

在上篇文章 揭祕 Android 系統啟動過程 中介紹到,init 程式啟動分為前後兩部分,前一部分是在核心啟動的,主要是完成建立和核心初始化工作,內容都是跟 Linux 核心相關的;後一部分是在使用者空間啟動的,主要完成 Android 系統的初始化工作。

Android 系統一般會在根目錄下放一個 init 的可執行檔案,也就是說 Linux 系統的 init 程式在核心初始化完成後,就直接執行 init 這個檔案,這個檔案的原始碼在 /system/core/init/init.cpp

init 程式

init 程式是 Linux 系統中使用者空間的第一個程式(pid = 1),我們熟悉的 App 應用程式都是以它為父程式的,init 程式入口函式是 main 函式。這個函式做的事情還是比較多的,主要分為三個部分:

  • init 程式第一階段
  • init 程式第二階段
  • init.rc 檔案解析

第一階段

我們先來看第一階段主要有以下內容:。

  • ueventd/watchdogd 跳轉及環境變數設定。
  • 掛載檔案系統並建立目錄。
  • 初始化日誌輸出、掛載分割槽裝置。
  • 啟用 SELinux 安全策略。
  • 開始第二階段前的準備。
//system/core/init/init.cpp

int main(int argc, char** argv) {
  if (!strcmp(basename(argv[0]), "ueventd")) {
    // 1 表示 true,也就執行 ueventd_main,ueventd
    // 主要是負責裝置節點的建立、許可權設定等一些列工作
    return ueventd_main(argc, argv);
  }
  // watchdogd 俗稱看門狗,用於系統出問題時重啟系統
  if (!strcmp(basename(argv[0]), "watchdogd")) {
    return watchdogd_main(argc, argv);
  }

  if (REBOOT_BOOTLOADER_ON_PANIC) {
    //初始化重啟系統的處理訊號,內部通過 sigaction 註冊訊號,
    //當監聽到該訊號時重啟系統
    install_reboot_signal_handlers();
  }
  //註冊環境變數PATH
  add_environment("PATH", _PATH_DEFPATH);
  // init 的 main 方法會執行兩次,由 is_first_stage 控制,
  // first_stage 就是第一階段要做的事
  bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
  // 只執行一次,因為在方法體中有設定 INIT_SECOND_STAGE
  if (is_first_stage) {
    // 清空檔案許可權
    umask(0);
    // on / and then we'll let the rc file figure out the rest.
    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    #define MAKE_STR(x) __STRING(x)
    mount("proc", "/proc", "proc", 0, 
          "hidepid=2,gid=" MAKE_STR(AID_READPROC));
    // Don't expose the raw commandline to unprivileged processes.
    chmod("/proc/cmdline", 0440);
    gid_t groups[] = { AID_READPROC };
    setgroups(arraysize(groups), groups);
    mount("sysfs", "/sys", "sysfs", 0, NULL);
    mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
    mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
    mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
    mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
    // 初始化日誌輸出
    InitKernelLogging(argv);

    LOG(INFO) << "init first stage started!";

    if (!DoFirstStageMount()) {
      LOG(ERROR) << "Failed to mount required partitions early ...";
      panic();
    }
    // 在刷機模式下初始化avb的版本,不是刷機模式直接跳過
    SetInitAvbVersionInRecovery();

    // 載入S ELinux policy,也就是安全策略
    selinux_initialize(true);

    // We're in the kernel domain, so re-exec init to transition to the init domain now
    // that the SELinux policy has been loaded.
    if (restorecon("/init") == -1) {
      PLOG(ERROR) << "restorecon failed";
      security_failure(); // 失敗則重啟系統
    }

    setenv("INIT_SECOND_STAGE", "true", 1);

    static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
    uint64_t start_ms = start_time.time_since_epoch().count()
      / kNanosecondsPerMillisecond;
    setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ms).c_str(), 1);

    char* path = argv[0];
    char* args[] = { path, nullptr };
    execv(path, args); // 重新執行 main 方法,進入第二階段

    // execv() only returns if an error happened, in which case we
    // panic and never fall through this conditional.
    PLOG(ERROR) << "execv(\"" << path << "\") failed";
    security_failure();
  }

  // ...
}
複製程式碼

init 程式第一階段做的主要工作是掛載分割槽,建立裝置節點和一些關鍵目錄,初始化日誌輸出系統,啟用 SELinux 安全策略。

第二階段

我們接著看第二階段,主要有以下內容:

  • 建立程式會話金鑰並初始化屬性系統。
  • 進行 SELinux 第二階段並恢復一些檔案安全上下文。
  • 新建 epoll 並初始化子程式終止訊號處理函式。
  • 設定其他系統屬性並開啟系統屬性服務。
//system/core/init/init.cpp

int main(int argc, char** argv) {
  // 同樣進行 ueventd/watchdogd 跳轉及環境變數設定
  // 之前準備工作時將 INIT_SECOND_STAGE設 置為 true,
  // 已經不為 nullptr,所以 is_first_stage 為 false
  bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
  // is_first_stage為false,直接跳過
  if (is_first_stage) {
    // ...
  }
  // 初始化日誌輸出
  InitKernelLogging(argv);
  // ...
  // 初始化屬性系統,並從指定檔案讀取屬性
  property_init();
  // ...
  // 初始化子程式退出的訊號處理函式
  signal_handler_init();
  // 載入 default.prop 檔案
  property_load_boot_defaults();
  export_oem_lock_status();
  // 啟動屬性伺服器
  start_property_service();
  set_usb_controller();
  //...
}
複製程式碼

init 程式第二階段主要工作是初始化屬性系統,解析 SELinux 的匹配規則,處理子程式終止訊號,啟動系統屬性服務,可以說每一項都很關鍵。如果說第一階段是為屬性系統、SELinux 做準備,那麼第二階段就是真正去把這些落實的。

解析 init.rc 檔案

//system/core/init/init.cpp

int main(int argc, char** argv) {
  // ...
  const BuiltinFunctionMap function_map;
  // 將 function_map 存放到 Action 中作為成員屬性
  Action::set_function_map(&function_map);
  // 解析 init.rc 檔案
  Parser& parser = Parser::GetInstance();
  parser.AddSectionParser("service",std::make_unique<ServiceParser>());
  parser.AddSectionParser("on", std::make_unique<ActionParser>());
  parser.AddSectionParser("import", std::make_unique<ImportParser>());
  std::string bootscript = GetProperty("ro.boot.init_rc", "");
  // 如果 ro.boot.init_rc 沒有對應的值,
  // 則解析 /init.rc 以及 /system/etc/init、/vendor/etc/init、
  // /odm/etc/init 這三個目錄下的 .rc 檔案
  if (bootscript.empty()) {
    parser.ParseConfig("/init.rc");
    parser.set_is_system_etc_init_loaded(
      parser.ParseConfig("/system/etc/init"));
    parser.set_is_vendor_etc_init_loaded(
      parser.ParseConfig("/vendor/etc/init"));
    parser.set_is_odm_etc_init_loaded(
      parser.ParseConfig("/odm/etc/init"));
  } else { // 如果 ro.boot.init_rc 屬性有值就解析屬性值
    parser.ParseConfig(bootscript);
    parser.set_is_system_etc_init_loaded(true);
    parser.set_is_vendor_etc_init_loaded(true);
    parser.set_is_odm_etc_init_loaded(true);
  }

  // ...
  ActionManager& am = ActionManager::GetInstance();
  am.QueueEventTrigger("early-init");

  // 等冷插拔裝置初始化完成
  am.QueueBuiltinAction(wait_for_coldboot_done_action,
                        "wait_for_coldboot_done");
  // ... so that we can start queuing up actions that require stuff from /dev.
  am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, 
                        "mix_hwrng_into_linux_rng");
  am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
  am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
  // 裝置組合鍵的初始化操作
  am.QueueBuiltinAction(keychord_init_action, "keychord_init");
  // 螢幕上顯示 Android 靜態 Logo,很熟悉的感覺有沒有
  am.QueueBuiltinAction(console_init_action, "console_init");

  // Trigger all the boot actions to get us started.
  am.QueueEventTrigger("init");

  // 執行 rc 檔案中觸發器為 on init 的語句
  am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, 
                        "mix_hwrng_into_linux_rng");

  // 當處於充電模式,則 charger 加入執行佇列,否則 late-init 加入佇列。
  std::string bootmode = GetProperty("ro.bootmode", "");
  if (bootmode == "charger") {
    am.QueueEventTrigger("charger");
  } else {
    am.QueueEventTrigger("late-init"); // 觸發 late-init
  }

  // 觸發器為屬性是否設定
  am.QueueBuiltinAction(queue_property_triggers_action, 
                        "queue_property_triggers");

  while (true) {
    // By default, sleep until something happens.
    int epoll_timeout_ms = -1;

    if (!(waiting_for_prop 
          || ServiceManager::GetInstance().IsWaitingForExec())) {
      am.ExecuteOneCommand();
    }
    if (!(waiting_for_prop 
          || ServiceManager::GetInstance().IsWaitingForExec())) {
      // 根據需要重啟服務  
      restart_processes();

      // If there's a process that needs restarting, wake up in time for that.
      if (process_needs_restart_at != 0) {
        epoll_timeout_ms =
          (process_needs_restart_at - time(nullptr)) * 1000;
        if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
      }

      // If there's more work to do, wake up again immediately.
      if (am.HasMoreCommands()) epoll_timeout_ms = 0;
    }

    epoll_event ev;
    // 迴圈等待事件發生
    int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, 
                                           epoll_timeout_ms));
    if (nr == -1) {
      PLOG(ERROR) << "epoll_wait failed";
    } else if (nr == 1) {
      ((void (*)()) ev.data.ptr)();
    }
  }

  return 0;
}
複製程式碼

這一階段 init 程式做了許多重要的事情,比如解析 init.rc 檔案,這裡配置了所有需要執行的 action 和需要啟動的 service,init 程式根據語法一步步去解析 init.rc,將這些配置轉換成一個個陣列、佇列,然後開啟無限迴圈去處理這些陣列、佇列中的 command 和 service,並且通過 epoll 監聽子程式結束和屬性設定。

init.rc 檔案

init.rc 檔案是 Android 系統的重要配置檔案,位於 /system/core/rootdir/ 目錄中。 主要功能是定義了系統啟動時需要執行的一系列 action 及執行特定動作、設定環境變數和屬性和執行特定的 service。

//system/core/rootdir/init.rc

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc // 稍後分析

on early-init
    // ... 
on init
    // ...
on late-init
    // ...
    trigger zygote-start
on post-fs // 掛載檔案系統
    load_system_props
    # start essential services
    start logd
    // 熟悉的 servermanager,後面章節再討論
    start servicemanager
    start hwservicemanager
    start vndservicemanager
    // ...
on post-fs-data // 掛載 data
    # We chown/chmod /data again so because mount is run as root + defaults
    chown system system /data
    chmod 0771 /data
    # We restorecon /data in case the userdata partition has been reset.
    restorecon /data

    # Make sure we have the device encryption key.
    start vold
    // ...

# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted // 啟動 zygote,稍後分析
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=unsupported
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on boot
    // ...
    # Start standard binderized HAL daemons
    class_start hal

    class_start core

複製程式碼

init 程式會解析 .rc 檔案,然後得到一些 service 去啟動,這些 service 通常不是普通的服務,文件裡面的稱呼是daemon(守護程式)。

所謂守護程式就是這些服務程式會在系統初始化時啟動,並一直執行於後臺,直到系統關閉時終止。

到這裡 init 程式的主要流程已經分析完了,我們總結下 init 程式啟動主要做了哪些工作。

init 程式啟動流程

首先在 Kernel 核心載入完後會呼叫 /system/core/init/init.cpp 檔案中的 main() 方法。該方法執行分為三個階段,前兩個的階段都是初始化環境,我們主要關注下第三個階段 解析 .rc 檔案。

在第三階段中通過解析 .rc 檔案啟動了 servicemanagerzygote 等服務,最後 init 程式進入了 loop。

zygote 程式

zygote 程式就是 daemon 其中之一,zygote 程式主要負責建立 Java 虛擬機器,載入系統資源,啟動 SystemServer 程式,以及在後續執行過程中啟動普通的應用程式。

zygote 程式啟動流程

在 init.rc 檔案頭部有這麼一句:

import /init.${ro.zygote}.rc
複製程式碼

其中 ${ro.zygote} 會被替換成 ro.zyogte 的屬性值,這個是由不同的硬體廠商自己定製的。 有四個值:zygote32、zygote64、zygote32_64、zygote64_32 ,也就是說可能有四種 .rc 檔案,分別是:

  • init.zygote32.rc:zygote 程式對應的執行程式是 app_process(純 32bit 模式)。
  • init.zygote64.rc:zygote 程式對應的執行程式是 app_process64(純 64bit 模式)。
  • init.zygote32_64.rc:啟動兩個 zygote 程式(名為 zygote 和 zygote_secondary),對應的執行程式分別是 app_process32(主模式)、app_process64。
  • init.zygote64_32.rc:啟動兩個 zygote 程式(名為 zygote 和 zygote_secondary),對應的執行程式分別是 app_process64(主模式)、app_process32。

為什麼要定義這麼多種情況呢?直接定義一個不就好了,這主要是因為 Android 5.0 以後開始支援 64 位程式,為了相容 32 位和 64 位才這樣定義。

不同的 zygote.rc 內容大致相同,主要區別體現在啟動的是 32 位,還是 64 位的程式。init.zygote32_64.rc 和 init.zygote64_32.rc 會啟動兩個程式,且存在主次之分。我們以init.zygote64_32.rc 為例。

//system/core/rootdir/init.zygote64_32.rc

// 程式名稱是 zygote,執行的二進位制檔案在 /system/bin/app_process64,稍後分析
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    user root
    group root readproc
    //建立一個 socket,名字叫 zygote,以 tcp 形式
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks
    
// 另一個 service,名字 zygote_secondary
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
    class main
    priority -20
    user root
    group root readproc
    socket zygote_secondary stream 660 root system
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks
複製程式碼

在 init.rc 檔案中可以找到一句 start zygote,這是呼叫 zygote 服務的啟動方式。

//system/core/rootdir/init.rc

// ...
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
// 啟動 zygote,稍後分析
on zygote-start && property:ro.crypto.state=unencrypted
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=unsupported
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start netd
    start zygote
    start zygote_secondary
複製程式碼

在 init.zygote64_32.rc 檔案中的頭部我們可以看到 zygote 對應的二進位制檔案是 /system/bin/app_process64 (以此為例),我們看一下對應的mk檔案, 對應的目錄在 platform/frameworks/base/cmds/app_process/Android.mk,其實不管是 app_process、app_process32 還是 app_process64,對應的原始檔都是 app_main.cpp。

//frameworks/base/cmds/app_process/Android.mk

// ...
app_process_src_files := \
    app_main.cpp \
// ...
LOCAL_SRC_FILES:= $(app_process_src_files)
// ...
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64
// ...
複製程式碼

app_main.cpp

在 app_main.cpp 的 main 函式中,主要做的事情就是引數解析。 這個函式有兩種啟動模式:

  • 一種是 zygote 模式,也就是初始化 zygote 程式,傳遞的引數有 --start-system-server --socket-name=zygote,前者表示啟動 SystemServer,後者指定 socket 的名稱。
  • 一種是 application 模式,也就是啟動普通應用程式,傳遞的引數有 class 名字以及 class 帶的引數。

兩者最終都是呼叫 AppRuntime 物件的 start 函式,載入 ZygoteInit 或 RuntimeInit 兩個 Java 類,並將之前整理的引數傳入進去。

//frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[]) {
  // 將引數 argv 放到 argv_String 字串中,然後列印出來
  if (!LOG_NDEBUG) { 
    String8 argv_String;
    for (int i = 0; i < argc; ++i) {
      argv_String.append("\"");
      argv_String.append(argv[i]);
      argv_String.append("\" ");
    }
  }

  AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
  // Process command line arguments
  // ignore argv[0]
  argc--;
  argv++;

  // 所有在 "--" 後面的非 "-" 開頭的引數都將傳入 vm, 
  // 但是有個例外是 spaced commands 陣列中的引數
  const char* spaced_commands[] = { "-cp", "-classpath" };
  // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
  bool known_command = false;

  int i;
  for (i = 0; i < argc; i++) {
    // 將 spaced_commands 中的引數額外加入 VM
    if (known_command == true) {
      runtime.addOption(strdup(argv[i]));
      known_command = false;
      continue;
    }

    for (int j = 0;
         j < static_cast<int>(sizeof(spaced_commands) 
                              / sizeof(spaced_commands[0]));
         ++j) {
      if (strcmp(argv[i], spaced_commands[j]) == 0) {
        known_command = true;
      }
    }

    if (argv[i][0] != '-') {
      break;
    }
    if (argv[i][1] == '-' && argv[i][2] == 0) {
      ++i; // Skip --.
      break;
    }

    runtime.addOption(strdup(argv[i]));
  }

  // Parse runtime arguments.  Stop at first unrecognized option.
  bool zygote = false;
  bool startSystemServer = false;
  bool application = false;
  String8 niceName;
  String8 className;

  ++i;  // Skip unused "parent dir" argument.
  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 啟動模式,也就是普通應用程式
      application = true;
    } else if (strncmp(arg, "--nice-name=", 12) == 0) {
      // 程式別名
      niceName.setTo(arg + 12);
    } else if (strncmp(arg, "--", 2) != 0) {
      // application 啟動的 class
      className.setTo(arg);
      break;
    } else {
      --i;
      break;
    }
  }

  Vector<String8> args;
  if (!className.isEmpty()) {
    // className 不為空,說明是 application 啟動模式
    args.add(application ? String8("application") : String8("tool"));
    // 將 className 和引數設定給 runtime
    runtime.setClassNameAndArgs(className, argc - i, argv + i);

    if (!LOG_NDEBUG) {
      String8 restOfArgs;
      char* const* argv_new = argv + i;
      int argc_new = argc - i;
      for (int k = 0; k < argc_new; ++k) {
        restOfArgs.append("\"");
        restOfArgs.append(argv_new[k]);
        restOfArgs.append("\" ");
      }
    }
  } else { // zygote 啟動模式
    // We're in zygote mode.
    maybeCreateDalvikCache(); // 新建 Dalvik 的快取目錄

    if (startSystemServer) { // 加入 start-system-server 引數
      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);
    args.add(abiFlag); // 加入 --abi-list= 引數

    // In zygote mode, pass all remaining arguments to the zygote
    // main() method.
    for (; i < argc; ++i) {
      args.add(String8(argv[i]));
    }
  }

  if (!niceName.isEmpty()) { // 設定程式別名
    runtime.setArgv0(niceName.string(), true /* setProcName */);
  }

  if (zygote) { // 如果是 zygote 啟動模式,則載入 ZygoteInit
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
  } else if (className) {
    // 如果是 application 啟動模式,則載入 RuntimeInit
    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.");
  }
}
複製程式碼

我們看到,在最後呼叫的是 runtime.start 函式,這個就是要啟動虛擬機器了,接下來我們分析 start 函式。

建立虛擬機器

//frameworks/base/core/jni/AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, 
                           const Vector<String8>& options, bool zygote) {
  // ...
  // 列印一些日誌,獲取 ANDROID_ROOT 環境變數
  // ...

  /* start the virtual machine */
  JniInvocation jni_invocation;
  // 初始化JNI,載入 libart.so
  jni_invocation.Init(NULL);
  JNIEnv* env;
  // 建立虛擬機器
  if (startVm(&mJavaVM, &env, zygote) != 0) {
    return;
  }
  // 表示虛擬建立完成,但是裡面是空實現
  onVmCreated(env);

  /*
   * Register android functions.
   * 註冊 JNI 函式
   */
  if (startReg(env) < 0) {
    ALOGE("Unable to register all android natives\n");
    return;
  }
  // JNI 方式呼叫 ZygoteInit 類的 main 函式
  // ...
}
複製程式碼

虛擬機器建立完成後,我們就可以用 JNI 反射呼叫 Java 了,其實接下來的語法用過 JNI 的都應該比較熟悉了,直接是 CallStaticVoidMethod 反射呼叫 ZygoteInit 的 main 函式。

//frameworks/base/core/jni/AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options,
                           bool zygote) {
  // 接下來的這些語法大家應該比較熟悉了,都是 JNI 裡的語法,
  // 主要作用就是呼叫 ZygoteInit 類的 main 函式 
  jclass stringClass;
  jobjectArray strArray;
  jstring classNameStr;

  stringClass = env->FindClass("java/lang/String");
  assert(stringClass != NULL);
  strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
  assert(strArray != NULL);
  classNameStr = env->NewStringUTF(className);
  assert(classNameStr != NULL);
  env->SetObjectArrayElement(strArray, 0, classNameStr);

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

  /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
  // 將 "com.android.internal.os.ZygoteInit" 
  // 轉換為 "com/android/internal/os/ZygoteInit"
  char* slashClassName = toSlashClassName(className);
  // 找到 class
  jclass startClass = env->FindClass(slashClassName);
  if (startClass == NULL) {
    ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
    /* keep going */
  } else {
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                                  "[Ljava/lang/String;)V");
    if (startMeth == NULL) {
      ALOGE("JavaVM unable to find main() in '%s'\n", className);
      /* keep going */
    } else {// 呼叫 ZygoteInit.main() 方法
      env->CallStaticVoidMethod(startClass, startMeth, strArray);

      #if 0
      if (env->ExceptionCheck())
        threadExitUncaughtException(env);
      #endif
    }
  }
  free(slashClassName);

  ALOGD("Shutting down VM\n");
  // 退出當前執行緒
  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");
}
複製程式碼

zygote 程式啟動主要建立了 Java 虛擬機器,有了虛擬機器,就可以執行 Java 程式碼了。

ZygoteInit.java

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

public static void main(String argv[]) {
  ZygoteServer zygoteServer = new ZygoteServer();

  // 標記 zygote 程式已啟動
  ZygoteHooks.startZygoteNoThreadCreation();

  // 進入 Zygote 程式
  try {
    Os.setpgid(0, 0);
  } catch (ErrnoException ex) {
    throw new RuntimeException("Failed to setpgid(0,0)", ex);
  }

  try {
    RuntimeInit.enableDdms(); // 開啟 DDMS 功能

    boolean startSystemServer = false;
    String socketName = "zygote";
    String abiList = null;
    boolean enableLazyPreload = false;
    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.");
    }

    // 為 zygote 註冊 socket
    zygoteServer.registerServerSocket(socketName);
    // 預載入處理
    if (!enableLazyPreload) {
      // zygote 預載入,下面介紹
      preload(bootTimingsTraceLog);
    } else {
      Zygote.resetNicePriority();
    }

    gcAndFinalize(); // GC 操作

    // Zygote process unmounts root storage spaces.
    Zygote.nativeUnmountStorageOnInit();

    // Set seccomp policy
    Seccomp.setPolicy();

    ZygoteHooks.stopZygoteNoThreadCreation();

    if (startSystemServer) { // 啟動 system_server,下面介紹
      startSystemServer(abiList, socketName, zygoteServer);
    }

    // 進入迴圈模式,下面介紹
    zygoteServer.runSelectLoop(abiList);

    zygoteServer.closeServerSocket();
  } catch (Zygote.MethodAndArgsCaller caller) {
    caller.run();
  } catch (Throwable ex) {
    zygoteServer.closeServerSocket();
    throw ex;
  }
}
複製程式碼

這裡 startSystemServer() 方法會丟擲一個 Zygote.MethodAndArgsCaller 異常,然後呼叫到 caller.run(),這裡會在 SystemServer 章節繼續分析。

preload()

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

static void preload(BootTimingsTraceLog bootTimingsTraceLog) {
  // ...
  // 預載入位於 /system/etc/preloaded-classes 檔案中的類
  preloadClasses();
  // 預載入資源,包含 drawable 和 color 資源
  preloadResources();
  // 預載入 OpenGL
  preloadOpenGL();
  // 通過 System.loadLibrary() 方法,
  // 預載入 "android","compiler_rt","jnigraphics" 這 3 個共享庫
  preloadSharedLibraries();
  // 預載入 文字連線符資源
  preloadTextResources();
  // 僅用於 zygote 程式,用於記憶體共享的程式
  WebViewFactory.prepareWebViewInZygote();
  endIcuCachePinning();
  warmUpJcaProviders();
  Log.d(TAG, "end preload");

  sPreloadComplete = true;
}
複製程式碼

執行 zygote 程式的初始化,對於類載入,採用反射機制 Class.forName() 方法來載入。對於資源載入,主要是 com.android.internal.R.array.preloaded_drawables 和 com.android.internal.R.array.preloaded_color_state_lists,在應用程式中以 com.android.internal.R.xxx 開頭的資源,便是此時由 Zygote 載入到記憶體的。

zygote 程式內載入了 preload() 方法中的所有資源,當需要 fork 新程式時,採用 copy on write 技術,如下:

zygote 程式 fork 程式

startSystemServer()

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

private static boolean startSystemServer(String abiList, 
                       String socketName, ZygoteServer zygoteServer)
    throws Zygote.MethodAndArgsCaller, RuntimeException {
  long capabilities = posixCapabilitiesAsBits(
    OsConstants.CAP_IPC_LOCK,
    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_PTRACE,
    OsConstants.CAP_SYS_TIME,
    OsConstants.CAP_SYS_TTY_CONFIG,
    OsConstants.CAP_WAKE_ALARM
  );
  /* Containers run without this capability, so avoid setting it in that case */
  if (!SystemProperties.getBoolean(PROPERTY_RUNNING_IN_CONTAINER,
                                   false)) {
    capabilities |= 
      posixCapabilitiesAsBits(OsConstants.CAP_BLOCK_SUSPEND);
  }
  /* Hardcoded command line to start the system server */
  // 準備引數
  String args[] = {
    "--setuid=1000",
    "--setgid=1000",
    "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
    "--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);

    /* Request to fork the system server process */
    // 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);
  }

  /* For child process */
  // 進入子程式 system_server
  if (pid == 0) {
    if (hasSecondZygote(abiList)) {
      waitForSecondaryZygote(socketName);
    }

    zygoteServer.closeServerSocket();
    // 完成 system_server 程式剩餘的工作
    handleSystemServerProcess(parsedArgs);
  }

  return true;
}
複製程式碼

可以看到 zygote 程式 fork 出一個新程式名為 system_server 也就是 SystemServer 程式。該方法是 SystemServer 程式啟動的起點,關於 SystemServer 啟動流程我們在後面的章節在討論,這裡先分析完 zygote 的主要流程。

runSelectLoop()

//frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
  ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
  ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
  // mServerSocket 是 socket 通訊中的服務端,即 zygote 程式。儲存到 fds[0]
  fds.add(mServerSocket.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 {
      // 處理輪詢狀態,當 pollFds 有事件到來則往下執行,否則阻塞在這裡
      Os.poll(pollFds, -1);
    } catch (ErrnoException ex) {
      throw new RuntimeException("poll failed", ex);
    }
    for (int i = pollFds.length - 1; i >= 0; --i) {
      // 採用 I/O 多路複用機制,當接收到客戶端發出連線請求
      // 或者資料處理請求到來,則往下執行;
      // 否則進入continue,跳出本次迴圈。
      if ((pollFds[i].revents & POLLIN) == 0) {
        continue;
      }
      if (i == 0) {
        ZygoteConnection newPeer = acceptCommandPeer(abiList);
        peers.add(newPeer);
        fds.add(newPeer.getFileDesciptor());
      } else {
        // i>0,則代表通過 socket 接收來自對端的資料,並執行相應操作
        boolean done = peers.get(i).runOnce(this);
        if (done) {
          peers.remove(i);
          fds.remove(i); // 處理完則從 fds 中移除該檔案描述符
        }
      }
    }
  }
}
複製程式碼

zygote 採用高效的 I/O 多路複用機制,保證在沒有客戶端連線請求或資料處理時休眠,否則響應客戶端的請求。在呼叫 runSelectLoop() 後 zygote 進入了輪詢狀態,隨時待命當接收到請求建立新程式請求時,立即喚醒並執行相應工作。

總結

到這裡 zygote 程式已經啟動完成了,Android 系統到目前已經啟動了第一個使用者程式 zygote。我們來回顧下目前 Android 系統啟動的流程:

zygote 啟動流程

當我們按下電源按鍵後會啟動 Bootloader,Bootloader 會載入 Kernel 核心到記憶體中,然後會啟動核心中的 idle 程式,idle 程式會啟動 kthreadd 和 init 兩個程式。

init 程式在啟動的時候會初始化執行環境,然後解析 init.rc 檔案,解析 init.rc 檔案的過程中會啟動 servicemanager、zygote 等服務,最後進入 loop 狀態。

zygote 服務啟動時會建立 Java 虛擬機器並初始化 Java 執行環境,然後啟動 SystemServer 服務,最後進入 loop 狀態。

這篇文章我們分析到這裡,下一篇我們接著分析 zygote 程式啟動中呼叫 startSystemServer() 方法後 SystemServer 程式啟動的流程。

參考資料

我的 Github

github.com/jeanboydev/…

我的公眾號

歡迎關注我的公眾號,分享各種技術乾貨,各種學習資料,職業發展和行業動態。

Android 波斯灣

相關文章