图解 Android 系列(二)深入理解 init 与 zygote 进程

介绍

这是一个连载的系列「图解 Android 系列」,我将持续为大家提供尽可能通俗易懂的 Android 源码分析。

所有引用的源码片段,我都会在第一行标明源文件完整路径。为了文章篇幅考虑源码中间可能有删减,删减部分会用省略号代替。

本系列源码基于:Android Oreo(8.0)

init 进程

在上篇文章 揭秘 Android 系统启动过程 中介绍到,init 进程启动分为前后两部分,前一部分是在内核启动的,主要是完成创建和内核初始化工作,内容都是跟 Linux 内核相关的;后一部分是在用户空间启动的,主要完成 Android 系统的初始化工作。

Android 系统一般会在根目录下放一个 init 的可执行文件,也就是说 Linux 系统的 init 进程在内核初始化完成后,就直接执行 init 这个文件,这个文件的源代码在 /system/core/init/init.cpp

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());
  parser.AddSectionParser("on", std::make_unique());
  parser.AddSectionParser("import", std::make_unique());
  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 进程启动主要做了哪些工作。

首先在 Kernel 内核加载完后会调用 /system/core/init/init.cpp 文件中的 main() 方法。该方法执行分为三个阶段,前两个的阶段都是初始化环境,我们主要关注下第三个阶段 解析 .rc 文件。

在第三阶段中通过解析 .rc 文件启动了 servicemanagerzygote 等服务,最后 init 进程进入了 loop。

zygote 进程

zygote 进程就是 daemon 其中之一,zygote 进程主要负责创建 Java 虚拟机,加载系统资源,启动 SystemServer 进程,以及在后续运行过程中启动普通的应用程序。

在 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(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 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& 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& 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 技术,如下:

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 fds = new ArrayList();
  ArrayList peers = new ArrayList();
  // 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 系统启动的流程:

当我们按下电源按键后会启动 Bootloader,Bootloader 会加载 Kernel 内核到内存中,然后会启动内核中的 idle 进程,idle 进程会启动 kthreadd 和 init 两个进程。

init 进程在启动的时候会初始化运行环境,然后解析 init.rc 文件,解析 init.rc 文件的过程中会启动 servicemanager、zygote 等服务,最后进入 loop 状态。

zygote 服务启动时会创建 Java 虚拟机并初始化 Java 运行环境,然后启动 SystemServer 服务,最后进入 loop 状态。

这篇文章我们分析到这里,下一篇我们接着分析 zygote 进程启动中调用 startSystemServer() 方法后 SystemServer 进程启动的流程。

参考资料

  • Android内核开发:图解Android系统的启动过程
  • Android 8.0 : 系统启动流程之Linux内核
  • Android bootloader/fastboot mode and recovery mode explained/Android boot process
  • Android is NOT just 'Java on Linux'
  • Android 启动流程简介

我的 Github

github.com/jeanboydev/…

我的公众号

欢迎关注我的公众号,分享各种技术干货,各种学习资料,职业发展和行业动态。

技术交流群

欢迎加入技术交流群,来一起交流学习。

转载于:https://juejin.im/post/5cb80763e51d456e2907f22c

你可能感兴趣的:(图解 Android 系列(二)深入理解 init 与 zygote 进程)