Android Framework 之 Zygote

阅读须知

本文源码基于 Android 10

Questions

  • Zygote 是什么?有什么作用??
  • Zygote 是如何启动的,启动后做了哪些事情?
  • 为什么不用 SystemServer 孵化应用进程?
  • Zygote 的通信方式?为什么不使用 Binder 进行通信?

Zygote

Zygoteinit 孵化的一个进程。本文将从源码的角度对 Zygote 进行分析,包括 Zygote 是如何启动的,在 Native 层和 Java 层分别做了哪些事情。

本文主要流程如下图所示:

image.png

启动 Zygote

先来看看第一步启动 Zygote,这里主要做了两件事情:

  1. init 进程解析 init.rc 文件;
  2. 启动 Zygote 服务。
image.png

解析 init.rc 文件

linux 内核启动后会去创建 init 进程,init 进程是用户空间的第一个进程。

init 进程的入口函数在 Anroid 10 上做了调整,不再是之前的 system/core/init/init.cpp,变成了 system/core/init/main.cpp 中的 main() 函数。

// system/core/init/main.cpp

int main(int argc, char** argv) {
    if (argc > 1) {
        // 参数为 "second_stage",启动 init 进程第二阶段
        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }
    
    // 默认启动 init 进程第一阶段
    return FirstStageMain(argc, argv);
}

init 进程的启动分为两个阶段,因为本文主要是讲解 Zygote,这里就直接进入第二阶段 SecondStageMain()

// system/core/init/init.cpp

int SecondStageMain(int argc, char** argv) {
    // 解析 init.rc 文件
    LoadBootScripts(am, sm);
   
    std::string bootmode = GetProperty("ro.bootmode", "");
    if (bootmode == "charger") {
        // 添加 trigger charger
        am.QueueEventTrigger("charger");
    } else {
        // 添加 trigger late-init
        am.QueueEventTrigger("late-init");
    }
    // ...
    return 0;
}

init 进程的第二阶段,会通过 LoadBootScripts() 解析 init.rc 文件;然后添加一些 trigger,这些 trigger 会触发 init.rc 文件中对应的 Action,这里需要结合 init.rc 来看。

# system/core/rootdir/init.rc

# action 的一般结构为 
# on trigger [ <& trigger> ]*
#     command1
#     command2
#     ...
# 表示当满足 trigger 的时候依次执行下面的 command

# trigger charger
on property:sys.boot_from_charger_mode=1
    class_stop charger
    trigger late-init

# trigger late-init
on late-init
    trigger zygote-start

# trigger zygote-start
on zygote-start && property:ro.crypto.state=unencrypted
    # 重点是这里会 start zygote 服务
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=unsupported
    start zygote
    start zygote_secondary

on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
    start zygote
    start zygote_secondary

可以看到,在 SecondStageMain() 中添加的 trigger chargertrigger late-init 最终都会触发 start zygote 命令,没错,这个命令就是用于启动 Zygote 服务的。

启动 Zygote 服务

Zygote 服务定义在哪里呢?我们回到 init.rc 文件的最前面,会发现如下代码。

# system/core/rootdir/init.rc

import /init.${ro.zygote}.rc

可以发现它导入了 init.${ro.zygote}.rc 文件,其实就是不同的平台(32位,64位,32_64位和64_32位)上对应的 init.zygote.rc 文件,如下图所示。

image.png

解释一下,因为 Android 5.0 开始支持 64 位程序,为了兼容 32 位和 64 位才会这样定义。

这些文件含义如下:

  1. init.zygote32.rc 表示 Zygote 进程对应的执行程序是 app_process(纯 32 bit 模式);
  2. init.zygote64.rc 表示 Zygote 进程对应的执行程序是 app_process(纯 64 bit 模式);
  3. init.zygote32_64.rc 表示会启动两个 Zygote 进程(zygotezygote_secondary),对应的执行程序分别是 app_process32(主模式)、app_process64
  4. init.zygote64_32.rc 表示会启动两个 Zygote 进程(zygotezygote_secondary),对应的执行程序分别是 app_process64(主模式)、app_process32

这里以 init.zygote64_32.rc 为例,它的定义如下。

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

# service 的一般结构为 
# service [name] [pathname] [ ] *
#     option
#     option
#     ...
# 表示名称为 name 的服务,启动程序位于 pathname,传递参数为 [  ]*

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 reserved_disk
    # option socket 的一般结构为 socket [name] [  ]*,这里表示创建名称为 zygote 的 socket
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    # option onrestart 表示当 zygote 服务重启的时候需要做什么事情
    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
    # option writepid 表示创建子进程时,向 /dev/cpuset/foreground/tasks 写入 pid
    writepid /dev/cpuset/foreground/tasks

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 reserved_disk
    # 创建名为 zygote_secondary 的 socket
    socket zygote_secondary stream 660 root system
    socket usap_pool_secondary stream 660 root system
    # 当 zygote_secondary 重启的时候也要重启 zygote
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks

主要看第一行:

  • /system/bin/app_process64 表示启动 zygote 服务对应执行的二进制文件;
  • -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote 是传递给可执行文件的参数(--zygote 表示 zygote 模式,--start-system-server 表示启动 SystemServer,后面会说到,简单了解一下)。

这里的可执行文件 /system/bin/app_process64 在编译前对应的源码位于 frameworks/base/cmds/app_process 下,来看看下面的 Android.mk

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

app_process_src_files := \
    app_main.cpp \
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64

可以看到无论是 app_processapp_process32app_process64 对应的源文件都是 app_main.cpp,这也就是 Zygote Native 层的入口了。

Zygote Native 层

app_main.cppZygoteNative 的入口了。来看看其做了哪些事情:

  1. 解析参数;
  2. 创建虚拟机;
  3. 注册 JNI 函数;
  4. 查找 static main() 并调用。
image.png

解析参数

前面我们已经找到 Zygote Native 层的入口 app_main.cpp,来看看其 main()

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

int main(int argc, char* const argv[])
{
    // 这里会创建 AppRuntime 的对象,它是 AndroidRuntime 的子类
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    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 参数表示启动 zygote 进程
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            // --start-system-serve 参数表示启动 SystemServer
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            // --application 参数表示启动应用进程
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            // --nick-name 指定进程名
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            // 与 --application 配置,启动指定的类,application 启动的 class
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector args;
    if (!className.isEmpty()) {
        // --application 模式下
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);
    } else {
        // --zygote 模式下
        // 创建 dalvik 的缓存目录,并定义权限
        maybeCreateDalvikCache();

        // 如果指定了 --start-system-server
        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }

        // 添加 --abi-list 参数
        String8 abiFlag("--abi-list=");
        abiFlag.append(prop);
        args.add(abiFlag);

        // 添加未解析的参数
        for (; i < argc; ++i) {
            args.add(String8(argv[i]));
        }
    }

    if (!niceName.isEmpty()) {
        // 如果指定了 --nick-name,则修改进程名
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }

    if (zygote) {
        // zygote 模式,调用 AppRuntime 的 start(),启动 ZygoteInit
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        // application 模式,启动 RuntimeInit
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } 
}

这里首先创建 AppRuntime 对象,然后进行参数解析。

从参数解析可以看出它是有两种模式的:

  • zygote 模式,传递参数为 --zygote --start-system-server 也就是前面 init.zygote64_32.rc 传入的参数,这种模式主要是启动 SystemServer 进程;
  • application 模式,传递参数为 --className=xxx 以及 className 携带的参数,这种模式是启动应用进程。

主要分析 zygote 模式,application 模式后续分析应用进程启动的时候再进行说明。这里参数解析完成后,如果是 zygote 模式,会调用 AppRuntime.start(),并传入 com.android.internal.os.ZygoteInit 和解析好的参数,而 AppRuntimeAndroidRuntime 的子类,并且没有重写 start(),那我们直接看 AndroidRuntime.start() 即可。

创建虚拟机

start() 里面首先会创建虚拟机。

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

void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
    // 创建虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);
}

注册 JNI 函数

然后会注册 JNI 函数。

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

void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
    if (startReg(env) < 0) {
        return;
    }
}

int AndroidRuntime::startReg(JNIEnv* env) 
{
    // 注册 jni 函数
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
}

进入 Zygote Java 层

最后会查找并调用 com.android.internal.os.ZygoteInitstatic main(),进入到 Zygote Java 层。

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

// 找 ZygoteInit 的 static main()
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V");
    if (startMeth == null) {

    } else {
        // 找到 static main() 直接调用
        env->CallStaticVoidMethod(startClass, startMeth, strArray); 
    }
}

Zygote Java 层

走到 ZygoteInit.main() 也就进入到了 Java 层,来看看其做了哪些事情:

  1. 预加载;
  2. 创建 Zygote Server Socket
  3. 启动 SystemServer
  4. 等待和处理请求。
image.png

预加载

Java 层,Zygote 首先会通过 preload() 进行预加载,在这里会预加载一些常用的类(比如常用的 Activity),主题资源,库等。

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

public static void main(String argv[]) {
    // 进行预加载
    preload(bootTimingsTraceLog);
}

static void preload(TimingsTraceLog bootTimingsTraceLog) {
    // 预加载 /system/etc/preloaded-classes 文件里面定义的常用类(比如我们用到的 Activity,Fragment 等)
    preloadClasses();
    // 预加载系统资源
    preloadResources();
    // 硬件抽象层
    nativePreloadAppProcessHALs();
    // 图形驱动
    maybePreloadGraphicsDriver();
    // 加载必要的库
    preloadSharedLibraries();
    // 语言相关字符信息
    preloadTextResources();
    // webview
    WebViewFactory.prepareWebViewInZygote();
}

那为什么要进行预加载呢?

  • Android 通过 Zygote fork 的方式创建子进程,Zygote 进程预加载一些类和资源,这样在 fork 子进程的时候,只需要复制一份即可,这样就节省了子进程的启动时间
  • 根据 forkcopy-on-write 机制,有些类如果不做改变,甚至都不用复制,子进程可以和父进程共享这部分数据,从而省去不少内存的占用

创建 Zygote Server Socket

预加载完成后,会创建 Server SocketZygote 是使用的 Socket 进行通信的。

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

public static void main(String argv[]) {
    // 这里会创建 server socket
    ZygoteServer zygoteServer = null;
    zygoteServer = new ZygoteServer(isPrimaryZygote);
}

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

ZygoteServer(boolean isPrimaryZygote) {
    // 创建 ServerSocket
    mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
    mUsapPoolSocket =
            Zygote.createManagedSocketFromInitSocket(
                    Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);

    // android 10 的 usap 机制,感兴趣的可以了解下,就是会预创建 usap 进程,后面启动进程的时候使用这种方式会快一点
    fetchUsapPoolPolicyProps();
}

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

static LocalServerSocket createManagedSocketFromInitSocket(String socketName) {
    // 创建 server socket
    return new LocalServerSocket(fd);
}

//

public LocalServerSocket(FileDescriptor fd) throws IOException
{
    // 创建 server socket
    impl = new LocalSocketImpl(fd);
    // 监听50端口
    impl.listen(LISTEN_BACKLOG);
    localAddress = impl.getSockAddress();
}

启动 SystemServer

创建完 server socket 后,会通过 forkSystemServer() 启动 SysteServer,这里底层会调用 fork() 创建 syste_server 进程,关于 SystemServer 后面写的时候再做具体分析。

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

public static void main(String argv[]) {
    // 解析参数
    boolean startSystemServer = false;
    for (int i = 1; i < argv.length; i++) {
        if ("start-system-server".equals(argv[i])) {
            startSystemServer = true;
        }
    }

    if (startSystemServer) e
        // 通过 forkSystemServer() 启动 SystemServer,最终是通过 fork() 去创建的
        Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
        if (r != null) {
            r.run();
            return;
        }
    }
}

等待和处理请求

最后,Zygote 会调用 runSelectLoop() 开启循环等待和处理 socket 请求。

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

public static void main(String argv[]) {
    // 开启循环,等待和处理请求
    caller = zygoteServer.runSelectLoop(abiList);
}

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

Runnable runSelectLoop(String abiList) {
    while (true) {
        // 当 pollFDs 有事件到来则往下执行,否则阻塞在这里
        Os.poll(pollFDs, -1);
        
        while (--pollIndex >= 0) {
            if (pollIndex == 0) {
                // 请求到来的时候,创建 socket 连接
                ZygoteConnection newPeer = acceptCommandPeer(abiList);

            } else if (pollIndex < usapPoolEventFDIndex) {
                // 处理 socket 请求
                ZygoteConnection connection = peers.get(pollIndex);
                final Runnable command = connection.processOneCommand(this);
            }
        }
    }
}

private ZygoteConnection acceptCommandPeer(String abiList) {
    return createNewConnection(mZygoteSocket.accept(), abiList);
}

protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
        throws IOException {
    // 创建 socket 连接
    return new ZygoteConnection(socket, abiList);
}

当有请求到来的时候,会先通过 acceptCommandPeer() 创建 socket 连接(ZygoteConnection)。然后通过 processOneCommand() 处理 socket 请求,具体的源码在写应用启动流程的时候再详细分析,这里了解 Zygote 是如何进行通信的即可。

Answers

到此,Zygote 的分析就结束了。接下来我们得回过头看看前面提到的一些问题。

Zygote 是什么?有什么作用?

Zygote 是由 init 创建出来的进程,它负责启动 SysteServer,孵化应用进程。

Zygote 是如何启动的,启动后做了哪些事情?

linux 内核启动后,会创建 init 进程,init 进程启动后解析 init.rc 文件,触发 start zygote 命令启动 zygote 服务。

Zygote 启动后做的事情可以分为 Native 层和 Java 层。
Native 层:

  1. 创建虚拟机;
  2. 注册 JNI 函数;
  3. 查找并调用 ZygoteInitstatic main(),进入 Java 层。

Java 层:

  1. 预加载常用类,主题资源,库等;
  2. 创建 Zygote Server Socket
  3. 启动 SystemServer
  4. 循环等待和处理请求。

为什么不用 SystemServer 孵化应用进程?

应用在启动的时候需要做很多准备工作,包括启动虚拟机,加载常用类,主题资源等,这些在 Zygote 启动后就已经完成了,应用进程通过 zygote fork 的时候只需要复制一份即可,提高了应用进程的启动效率,而对于没有修改的类甚至不用复制,直接和 Zygote 共享,节省了应用进程的内存空间。

而对于 SystemServer,它会启动 Binder 线程,引导服务,核心服务和其他服务等,这些是不应该被继承到应用进程的。

所以,将 SystemServer 和应用进程都需要的资源单独放到 Zygote 里面进行处理,各自的事情等进程孵化出来后自己处理就好了。

Zygote 的通信方式?为什么不使用 Binder 进行通信?

Zygote 是使用的 Socket 进行的通信。

如果 Zygote 使用 Binder 进行通信,那么它需要启动 Binder 线程,打开 Binder 驱动,获取文件描述符,再通过 mmap 进行内存映射,还要创建 Binder 对象注册到 ServiceManager

试想一下 AMSZygote 发起启动应用进程请求的话,需要从 ServiceManager 中查询 ZygoteBinder 对象,然后再通过 Binder 调用,十分的繁琐。而且 ZygoteSystemServer 本来就是父子进程关系,简单的消息通信使用 Socket 非常方便。

你可能感兴趣的:(Android Framework 之 Zygote)