Android zygote 进程的启动过程分析

Posted 严振杰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android zygote 进程的启动过程分析相关的知识,希望对你有一定的参考价值。

版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com

zygote进程在 android 开发中属于比较底层的内容,然而要透彻的理解 AMS、WMS 和 IPC 等,不可避免的要先理解zogyte进程,因此本文即作为对自己的学习记录,也想分享出来和遇到同样问题的读者交流。

在正式内容开始之前,我建议读者朋友下载部分源代码备用,分别是:

  • platform/framework/base
  • platform/system/core
  • platform/packages/apps/Launcher3

我们可以到 Android 源码托管站下载:https://android.googlesource.com/,可以下载具体某个模块源代码,可以指定分支或者 tag,需要翻阅 GFW。

另外如果你没有翻阅 GFW 的条件的话,可以使用清华大学镜像源:
https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/

例如,现在我们从 Terminal 进入~/Workspance/Android目录下,然后分别下载上述三个模块oreo-release分支的源代码:

$ git clone -b oreo-release https://aosp.tuna.tsinghua.edu.cn/platform/frameworks/base platform_framework_base
...
$ git clone -b oreo-release https://aosp.tuna.tsinghua.edu.cn/platform/system/core platform_system_core
...
$ git clone -b oreo-release https://aosp.tuna.tsinghua.edu.cn/platform/packages/apps/Launcher3 platform_packages_apps_Launcher3

特别声明:本文出现的源代码,为了更加简洁清晰,部分代码经过大量的精简,读者阅读本文时,如果有条件,最好配合源代码,以免产生误解。

另外,纠正一个无伤大雅的小问题,我们经常说的init进程、zygote进程和system_server进程,它们的名字如上所写,不是大写,也不是驼峰式写法,比如这些写法是不正确的:

Init进程,正确的应该是:init进程
Zygote进程,正确的应该是:zygote进程
SystemServer进程,正确的应该是:system_server进程

本文内容

  • init.rc 配置文件的作用和运行流程
  • init.zygote.rc 配置文件的含义和作用
  • ZygoteInit.java 初始化zygote进程的过程
  • ZygoteInit.java 启动system_server进程的过程
  • SystemServer.java 初始化system_server进程的过程
  • zygote 进程等待 Socket 客户端的请求

init.rc 配置文件的作用和运行流程

Android 系统是基于 Linux 内核的,Linux 系统中的进程都是 init 进程的子进程,Android 的zygote进程也是在系统启动的过程,由init进程创建的。本小节要分析从系统启动后代码是如何执行到 zygote相关逻辑块的,然后由此引出zygote的启动过程分析。

系统启动时kernel/init/main.c文件中的kernel_init()函数会做一些硬件相关的基础工作,比如 CPU 初始化和内存初始化等,然后会执行platform/system/core目录下的init程序,该程序即 Android 的init进程对应的程序。该程序的对应代码是platform/system/core/init.cpp,打开该文件并找到它的入口函数:

// 注意这两行引入
#include "property_service.h"
#include "service.h"

int main(int argc, char** argv) 
  ... // 一些必要的环境变量配置和启动其他Linux进程

  // 执行在头部引入的property_service.h的start_property_service()函数
  start_property_service();

  // 解析配置文件
  Parser& parser = Parser::GetInstance();
  // 解析service
  parser.AddSectionParser("service",std::make_unique<ServiceParser>());
  parser.AddSectionParser("on", std::make_unique<ActionParser>());
  parser.AddSectionParser("import", std::make_unique<ImportParser>());

  parser.ParseConfig("/init.rc");
  ...

  ActionManager& am = ActionManager::GetInstance();

  ... // 配置am命令参数

  while (true) 
    // 使用epoll机制 + 死循环,使init进程一直运行,类似Looper#loop()

    // 默认一直阻塞,直到有事情需要做被唤醒
    int epoll_timeout_ms = -1;

    if (...) 
      am.ExecuteOneCommand();
      // 一些赋值逻辑,可能是0
      epoll_timeout_ms = ...;
    

    // 调用epoll_wait使init进程沉睡或者...
    epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms)

  return 0;

我们会发现该函数中解析了位于platform/system/core/rootdir/init.rc的初始化配置文件,我摘抄出了与zygote进程相关的部分代码:

import /init.$ro.zygote.rc

...

# 当电源键被按住的时候执行 property
on property:sys.boot_from_charger_mode=1
    class_stop charger
    trigger late-init

# 挂载文件系统并启动核心系统服务
on late-init
    ...
    # 调用zygote-start
    trigger zygote-start

    ...

    trigger early-boot
    trigger boot

# 在init.rc中调用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

上述配置的代码类似 C 语言风格,每一个on和后边的定义我们暂且把它叫做函数,当手机处于关机状态时,我们摁住电源键开机,init进程执行到这里时,会调用property函数,该函数做一些准备工作后,会调用late-init函数,late-init函数会做一些启动初始化相关的工作,完事以后会调用zygote-start去执行init.zygote*.rc,我们发现该配置文件在init.rc文件头部被引入:

import /init.$ro.zygote.rc

该文件被引入时有个ro.zygote变量,它可以被理解为一个环境变量,该变量的值和设备的 CPU 平台相关,同样在./rootdir目录下的可选项有:

init.zygote32.rc
init.zygote32_64.rc
init.zygote64.rc
init.zygote64_32.rc

为了一目了然简单清晰,我们选择init.zygote32.rc来分析,init进程执行到这里,根据init.zygote32.rc的描述,会 fork zygote进程,具体细节请看下一节。

init.zygote.rc 配置文件的含义和作用

打开init.zygote32.rc文件后我们发现它的代码很简洁清晰:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    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,在platform/core/init/README.md中有对init语法的描述,service的语法是这样的:

service <name> <pathname> [ <argument> ]*
    <option>
    <option>

其中对service的解释是:serviceinit退出时启动和重新启动的程序。我认为在这里,init会先 fork 一个进程,会把该进程命名为zygote,然后在该进程中执行/system/bin/app_process程序,并且带上参数--zygote -- start-system-server

后面的socket是为zygote进程创建一个名称为"zygote"的socket资源,在系统启动后,我们可以在/dev/socket目录下发现一个名为“zygote”的文件。在本文中,该配置被执行完后我们会做这个 Socket 资源的验证。

接下来,我们把目光集中在zygote进程接下来要执行的/system/bin/app_process程序上,system/bin一般用来存放系统命令类的程序,通过分析和查找,它对应的代码在platform/frameworks/base/cmds/app_process/app_main.cpp文件中,接下来我们详细分析该文件。

ZygoteInit.java 初始化 zygote 进程的过程

上面讲到init进程解析init.zygote.rc后,会 forkzygote进程,然后在zygote进程中执行/system/bin/app_process程序,执行该程序的目的是初始化zygote进程。

接下来我们打开platform/frameworks/base/cmds/app_process/app_main.cpp文件,来分析初始化zygote进程时做了哪些事情,目光定位在main()函数上:

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

    // 创建 AppRuntime的成员变量
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));

    ...

    // 解析运行时参数,赋值zygote和startSystemServer
    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 = 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;
        
    

    if (!className.isEmpty()) 
        ...
     else 
        ...
        if (startSystemServer) 
            args.add(String8("start-system-server"));
        
    

    if (zygote) 
        // 调用 ZygoteInit#main()
        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.");
    

上述代码,首先创建了一个AppRuntime的局部成员变量,然后对入参进行了解析,根据对init.zygote32.rc传入参数的分析,这里的zygote变量和startSystemServer的值均为true,因此在最后调用AppRuntime#start()函数时走了:

if (zygote) 
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

AppRuntime类也定义在platform/frameworks/base/cmds/app_process/app_main.cpp中:

class AppRuntime : public AndroidRuntime

  ...
;

发现AppRuntime类继承于AndroidRuntime,而AppRuntime类中是没有start()函数的,因此回到上方的main()函数中,实际上当执行AppRuntime#start()时实际上是执行了AndroidRuntime#start(),下面我们来看看AndroidRuntime类,它位于platform/frameworks/base/core/jni/AndroidRuntime.cpp

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

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

    ...

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;

    startVm(&mJavaVM, &env, zygote);
    onVmCreated(env);

    /*
     * Register android functions.
     */
    startReg(env);

    /*
     * We want to call main() with a String array with arguments in it.
     */
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

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

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
        "([Ljava/lang/String;)V");
    env->CallStaticVoidMethod(startClass, startMeth, strArray);

    ...

根据AndroidRuntime名称和上述代码可以分析得出,该函数启动了 Android 系统运行时库,其中它主要做了三件事:

  • 调用startVmVM()函数启动虚拟机
  • 调用startReg()函数注册 Android 的 Java 方法,其实在这里就是 JNI 方法
  • 反射调用 Java 类com.android.internal.os.ZygoteInit#main()方法,并把 fork zygote进程时的参数传入用来辅助初始化zygote进程

此时,我们回到启动zygote进程的地方回顾一下:

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

根据上述分析,此时调用句柄已经执行到com.android.internal.os.ZygoteInit#main(),该类的 Java 文件位于platform/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

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

    // 标记Zygote开始初始化,并Hook确保不创建其他线程
    ZygoteHooks.startZygoteNoThreadCreation();

    // Zygote进入自己的进程组
    try 
        Os.setpgid(0, 0);
     catch (ErrnoException ex) 
        throw new RuntimeException("Failed to setpgid(0,0)", ex);
    

    ...

    boolean startSystemServer = false;
    String socketName = "zygote";

    // 解析参数,初始化局部变量
    for (int i = 1; i < argv.length; i++) 
        if ("start-system-server".equals(argv[i])) 
            startSystemServer = true;
        
        ...
    

    // 注册一个名为zygote的ServerSocket,
    zygoteServer.registerServerSocket(socketName);

    // 第一次启动时会预加载资源
    if (!enableLazyPreload) 
        ...
        preload(bootTimingsTraceLog);
    
    ...

    // 停止Hook不创建其他线程,也就是说允许创建其他线程
    ZygoteHooks.stopZygoteNoThreadCreation();

    // 这里一定是true,启动SystemServer
    if (startSystemServer) 
        startSystemServer(abiList, socketName, zygoteServer);
    

    // 运行zygote进程的select循环
    zygoteServer.runSelectLoop(abiList);

    zygoteServer.closeServerSocket();

在这个初始化过程中,做了不少准备工作,更加详细的过程请阅读上述代码注释和源码,我们这里总结以下我们需要着重关注的几件事:

  1. 调用ZygoteServer#registerServerSocket()方法创建一个名为zygote的 Socket 资源
  2. 调用ZygoteInit#starSystemServer()方法启动system_server进程
  3. 调用ZygoteInit#runSelectLoop()方法循环接受 Socket 连接,在这里开始和 AMS 搭上边,其实这里就是循环等待 AMS 的连接和命令,更加详细的过程在下文中分析。

ZygoteServer#registerServerSocket()创建 Java 层的 ServerSocket 对象

上面说到ZygoteServer#registerServerSocket()创建了一个名为zygote的 Socket 资源,我们知道,Socket 是 IPC 通信的一种方式,该 Socket 资源在后期被用在和 AMS 等服务进行进程间通信。下面我们来看ZygoteServer#registerServerSocket()的具体实现,ZygoteServer.java位于com.android.internal.os.ZygoteServer.java

class ZygoteServer 

  private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";

  private LocalServerSocket mServerSocket;

  ZygoteServer() 
  

  void registerServerSocket(String socketName) 
      if (mServerSocket == null) 
          int fileDesc;

          // fullSocketname的值是ANDROID_SOCKET_zygote
          final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;

          // 获取Socket名称对应的环境变量
          String env = System.getenv(fullSocketName);
          fileDesc = Integer.parseInt(env);

          // 根据环境变量创建文件描述符
          FileDescriptor fd = new FileDescriptor();
          fd.setInt$(fileDesc);

          // 根据文件描述符创建Socket
          mServerSocket = new LocalServerSocket(fd);
    
  

请读者先看完上述代码的注释,上述代码中其他的因素都是确定的,只有 Socket 名称对应的环境变量是不确定的,这个环境变量指向的文件描述符的值是在文章开头的platform/system/init.cpp创建的,在文章开头其实我已经标识出来了,但是大多数人应该不知道是干嘛用的,我们回到init.cpp

// 注意这两行引入
#include "property_service.h"
#include "service.h"

int main(int argc, char** argv) 
  ... // 一些必要的环境变量配置和启动其他Linux进程

  // 执行在头部引入的property_service.h的start_property_service()函数
  start_property_service();

  // 解析配置文件
  Parser& parser = Parser::GetInstance();
  // 解析service
  parser.AddSectionParser("service",std::make_unique<ServiceParser>());
  ...

该函数中start_property_service()会去创建 Socket 资源,下面我们一步步看这些相关的文件。

platform/system/core/init/property_service.cpp中调用util#create_socket()函数:

#include "util.h"

void start_property_service() 
    property_set("ro.property_service.version", "2");

    // 创建Socket连接
    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | ...);
    ...

    register_epoll_handler(property_set_fd, handle_property_set_fd);

platform/system/core/init/util.cppcreate_socket()函数会在ANDROID_SOCKET_DIR常量指定的目录创建一个 Unix 的 Socket 资源,并通过ANDROID_SOCKET_ENV_PREFIX常量加[name]指定名称的环境变量向其他位置共享该文件描述符的值:

#include <socket.h>

/*
 * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
 * ("/dev/socket") as dictated in init.rc. This socket is inherited by the
 * daemon. We communicate the file descriptor's value via the environment
 * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
 */
int create_socket(const char *name, ...)

    ...

    android::base::unique_fd fd(socket(PF_UNIX, type, 0));
    ...

    return fd.release();

上述两个常量定义在platform/system/core/include/cutils/sockets.h中:

#define ANDROID*SOCKET_ENV_PREFIX "ANDROID_SOCKET*"
#define ANDROID_SOCKET_DIR "/dev/socket"

如果你手里有个 Android 系统的设备,我们可以查看一下该文件:

Harrys-MacBook-Pro:Visit Harry$ adb shell

shell@cancro:/ $ cd /dev/socket
shell@cancro:/dev/socket $ ls
adbd
...
zygote
shell@cancro:/dev/socket $

在上述com.android.internal.os.ZygoteServer.java中也能看到 Socket 资源的前缀常量,因此它在这里就获取到了名为ANDROID_SOCKET_zygote的文件描述符,并在 Java 层创建了LocalServerSocket的服务端 Socket 对象。

ZygoteInit.java 启动 system_server 进程的过程

zygote进程启动了 Java 层的LocalServerSocket后,接着调用了ZygoteInit#starSystemServer()启动system_server进程。

/**
  * Prepare the arguments and fork for the system server process.
  */
private static boolean startSystemServer(String socketName,
    ZygoteServer zygoteServer) 

  /* Hardcoded command line to start the system server */
  String args[] = 
    ...
    "--nice-name=system_server", // 指定进程名字 system_server
    "--runtime-args",
    "com.android.server.SystemServer", // 指定进程fork后要执行的程序
  ;

  ZygoteConnection.Arguments parsedArgs = new ZygoteConnection.Arguments(args);
  ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
  ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

  // 从zygote进程fork system_server进程
  int pid = Zygote.forkSystemServer(
      parsedArgs.uid, parsedArgs.gid,
      parsedArgs.gids,
      parsedArgs.debugFlags,
      null,
      parsedArgs.permittedCapabilities,
      parsedArgs.effectiveCapabilities);

  return true;

这段代码中指定了 fork 新进程的名称为system_server,当进程被 fork 后会调用com.android.server.SystemServer#main()方法。

先简单的看一下 fork 新进程的方法:

    /**
     * Fork system_server进程,并返回该进程的pid
     */
    public static int forkSystemServer(int uid, int gid, ...) 
        VM_HOOKS.preFork();

        //
        int pid = nativeForkSystemServer(uid, gid, ...);

        VM_HOOKS.postForkCommon();
        return pid;
    

    native private static int nativeForkSystemServer(int uid, int gid, ...);

上述代码也比较清晰明了,实际上是调用了native方法 fork 了一个新的进程。

SystemServer.java 初始化 system_server 进程的过程

接着会执行com.android.server.SystemServer#main()方法,我们看一下该方法:

public final class SystemServer 
  /**
    * The main entry point from zygote.
    */
  public static void main(String<

以上是关于Android zygote 进程的启动过程分析的主要内容,如果未能解决你的问题,请参考以下文章

Android zygote 进程的启动过程分析

android8.1启动过程 zygote进程分析2

Android 源码分析 应用程序启动的过程

Android5 Zygote 与 SystemServer 启动流程分析

Android5 Zygote 与 SystemServer 启动流程分析

Android5 Zygote 与 SystemServer 启动流程分析