Android系统启动(二) — Zygote进程启动过程

1 概述

Android 系统中,DVMDalvik 虚拟机)和 ART,系统服务进程 system_server 以及应用程序进程都是由 Zygote 进程来创建的(而 Native 程序,也就是 C/C++ 开发的程序则是由 init 进程创建启动的)。Zygote 进程也称孵化器,通过 fork(复制进程) 的形式来创建应用程序进程和 system_server 进程,由于 Zygote 进程在启动时会创建 DVM 或者 ART ,因此通过 fork 而创建的应用程序进程和 system_server 进程可以在内部获取一个 DVM 或者 ART 的实例副本。

Zygote 进程是在 init 进程启动时创建的,起初,Zygote 进程的名称并不是 Zygote,而是 app_process,这个名称是在 /frameworks/base/cmds/app_process/Android.mk 中定义的。Zygote 进程启动后,Linux 系统下的 pctrl 系统会调用 app_process,将其名称换成了 Zygote

Zygote 是一个 C/S模型。Zygote 进程作为服务端,通过 Socket 的方式和其他进程进行通信,这里的“其他进程”主要指的是系统服务进程 system_server

Linux 系统中,调用 fork 函数创建子进程的时候,不会复制父进程的内存,而是父子进程共享一个内存空间,只有当子进程或者父进程对内存数据进行修改时才会进行内存复制,从而,父进程和子进程才有各自的内存空间。在此之前,只会以只读的方式共享。

写时拷贝(copy-on-write):等到修改数据的时候才真正的分配内存空间,是一种可以推迟甚至避免拷贝数据的技术。这是对程序性能的优化,这样做的目的是为了避免不必要的拷贝。

Zygote 作为孵化器,可以提前加载一些资源,这样 fork 出的子进程就可以直接使用这些资源,而不用重新加载。比如,system_server 进程就可以直接使用 Zygote 进程中的 JNI 函数、共享库、常用类以及主题资源。

Zygote 进程启动工作了以下几件事:

  • 调用 AppRuntime/AndroidRuntime.start 方法,创建 Java 虚拟机并为其注册 JNI 方法;
  • 通过 JNI 调用 ZygoteInit.main 方法,从 Native 层进入 Java 框架层;
  • 启动 system_server 进程;
  • 通过 ZygoteServer 创建服务器端的 Socket,并通过 ZygoteServer.runSelectLoop 方法等待 AMS 的请求来创建新的应用程序进程;

以下是 Zygote 启动的时序图:

Zygote启动流程

2 Zygote 启动脚本

init进程启动过程 中讲到,init 进程会解析 init.rc 文件,然后创建和加载 service 字段指定的进程,Zygote 进程就是以这种方式被 init 进程加载的。在 init.rc 文件中采用 Import 类型语句来引入 Zygote 启动脚本,这些启动脚本都是由 Android 初始化语言(Android Init Language)来编写的:

// /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  // ${ro.zygote} 由厂商定义,与平台无关

可以看出,init.rc 不会直接引入一个固定的文件,而是根据 ro.zygote 的内容来引入不同的文件。

Android 5.0 开始,开始支持 64 位程序,Zygote 也就有了 32 位和 64 位的区别,所以用 ro.zygote 属性来控制使用不同的 Zygote 启动脚本,从而也就启动了不同版本的 Zygote 进程,从而也就启动了不同版本的 Zygote 进程,ro.zygote 属性的取值有以下 4 种:

  • init.zygote32.rcZygote 进程对应的执行程序是 app_process(纯 32 bit 模式);
  • init.zygote32_64.rc:启动两个 Zygote 进程(zygotezygote_secondary),对应的执行程序分别是 app_process32(主模式)、app_process64
  • init.zygote64.rcZygote 进程对应的执行程序是 app_process64(纯 64 bit 模式);
  • init.zygote64_32.rc:启动两个 Zygote 进程(zygotezygote_secondary),对应的执行程序分别是 app_process64(主模式)、app_process32

这些 Zygote 启动脚本都放在 system/core/rootdir 目录中,如下所示:

ro.zygotes属性

64 位处理器为例,init.zygote64_32.rc 的代码如下所示:

init.zygote64_32

脚本中有两个 Service 类型语句,说明会启动两个 Zygote 进程,一个名称为 zygote,执行程序为 app_process64,作为主模式;另一个名称为 zygote_secondary,执行程序为 app_process32,作为辅模式。其他的脚本文件与此类似。

init.zygote64_32.rc 为例,其启动文件地址为 /system/bin/app_process64(上图),app_process64 对应的代码在 /frameworks/base/cmds/app_process 的目录下。

首先,看一下 Android.mk

// /frameworks/base/cmds/app_process/Android.mk
LOCAL_PATH:= $(call my-dir)
....
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_prcessapp_prcess32 还是 app_prcess64,对应的源文件都是 app_main.cpp

3 Zygote 进程启动过程

init 启动 Zygote 进程时主要是通过调用 app_main.cpp 文件中的 main 函数,其中是通过调用 AppRuntimestart 方法来启动 Zygote 进程的:

// /frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
    
    
   ...
    while (i < argc) {
    
    
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
    
     // 1
            // 如果当前运行在Zygote进程中,则将zygote设置为true
            zygote = true; // 2
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
    
     // 3
            // 如果当前运行在system_server进程中,则将startSystemServer设置为true
            startSystemServer = true; // 4
        } 
        ...
    }
    ...
    if (!niceName.isEmpty()) {
    
    
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }
	// 如果运行在zygote进程中
    if (zygote) {
    
     // 5
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote); // 6
    } 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 runtime(argv[0], computeArgBlockSize(argc, argv));

Zygote 进程都是通过 fork 自身来创建子进程的,这样,Zygote 进程以及它的子进程都可以进入 app_main.cppmain 函数,因此,main 函数中为了区分当前运行在哪个进程, 会在注释 1 处判断参数 arg 中是否包含了 --zygote,如果包含了则说明 main 函数是运行在 Zygote 进程中的,并在注释 2 处将 zygote 设置为 true。同理在注释 3 处判断参数 arg 中是否包含 --start-system-server,如果包含了则说明 main 函数试运行在 system_server 进程中的,并在注释 4 处将 startSystemServer 设置为 true

注释 5 处,如果 zygotetrue,就说明当前运行在 zygote 进程中,就会调用注释 6 处的 AppRuntimestart 函数。AppRuntime 继承自 AndroidRuntime,且没有重写 start 方法,因此,Zygote 的流程进入了 AndroidRutime 中,代码如下所示:

// /frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    
    
    ...
    /* start the virtual machine 启动Java虚拟机 */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
    
     // 1
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions. 为Java虚拟机注册JNI方法
     */
    if (startReg(env) < 0) {
    
     // 2
        ALOGE("Unable to register all android natives\n");
        return;
    }

    ...
    // 从app_main的main函数得知className为com.android.internal.os.ZygoteInit
    classNameStr = env->NewStringUTF(className); // 3
    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.
     * 将className的"."替换为"/"
     */
    char* slashClassName = toSlashClassName(className != NULL ? className : ""); // 4
    // 找到 zygoteInit
    jclass startClass = env->FindClass(slashClassName); // 5
    if (startClass == NULL) {
    
    
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
    
    
        // 找到ZygoteInit的main函数
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                                                     "([Ljava/lang/String;)V"); // 6
        if (startMeth == NULL) {
    
    
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
    
    
            // 通过JNI调用ZygoteInit的main方法
            env->CallStaticVoidMethod(startClass, startMeth, strArray); // 7

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

在注释 1 出调用 startVm 函数来创建 Java 虚拟机,在注释 2 处调用 startReg 函数为 Java 虚拟机注册 JNI 方法。在注释 3 处的 className 的值是传进来的参数,它的值为 com.android.internal.os.ZygoteInit。在注释 4 处通过 toSlashClassName 函数,将 className. 替换成 /,替换后的值为 com/android/internal/os/ZygoteInit,并赋值给 slashClassName,接着在注释 5 处根据 slashClassName 找到 ZygoteInit,找到 ZygoteInit 后,在注释 6 处调用 ZygoteInit.main 方法。最终会在注释 7 处通过 JNI 调用 ZygoteInit.main 方法。这里为什么要用 JNI 呢?因为 ZygoteInit.main 方法是由 Java 语言编写的,当前的运行逻辑在 Native 中,这就需要通过 JNI 来调用 Java。这样,Zygote 就从 Native 层进入了 Java 框架层。

通过 JNI 调用 ZygoteInit.main 方法后,Zygote 就进入了 Java 框架层,此前是没有任何代码进入 Java 框架层的,换句话说是 Zygote 开创了 Java 框架层,ZygoteInit.main 方法代码如下:

// /frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
    
    
    // ZygoteServer是Zygote进程的Socket通讯服务端的管理类
    ZygoteServer zygoteServer = null;
    ...
    Runnable caller;
    try {
    
    
        if (!enableLazyPreload) {
    
    
            bootTimingsTraceLog.traceBegin("ZygotePreload");
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                                SystemClock.uptimeMillis());
            preload(bootTimingsTraceLog); // 1
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                                SystemClock.uptimeMillis());
            bootTimingsTraceLog.traceEnd(); // ZygotePreload
        } else {
    
    
            Zygote.resetNicePriority();
        }
        
        ...
        zygoteServer = new ZygoteServer(isPrimaryZygote); // 2
		
        if (startSystemServer) {
    
    
            Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer); // 3
            // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
            // child (system_server) process.
            if (r != null) {
    
    
                r.run();
                return;
            }
        }

        Log.i(TAG, "Accepting command socket connections");

        // The select loop returns early in the child process after a fork and
        // loops forever in the zygote.
        caller = zygoteServer.runSelectLoop(abiList); // 4
    } catch (Throwable ex) {
    
    
        Log.e(TAG, "System zygote died with exception", ex);
        throw ex;
    } finally {
    
    
        if (zygoteServer != null) {
    
    
            zygoteServer.closeServerSocket();
        }
    }
	...
}

注释 1 处预加载类和资源。注释 2 处创建一个 Server 端的 Socket 通道,用于响应ActivityManagerService 请求 Zygote 来创建新的应用程序进程的请求。注释 3 处启动 system_server 进程,这样系统的服务也会由 system_server 进程启动起来。注释 4 处调用 ZygoteServer.runSelectLoop 方法来等待 AMS 创建新进程的请求。

由以上可知,ZygoteInit.main 方法中主要做了 4 件事情:

  • 预加载类和资源
  • 创建 Server 端的 Socket ,用于响应 ActivityManagerService 创建新的应用进程的请求;
  • 启动 system_server 进程,这样系统服务也就会由 system_server 启动起来;
  • 等待 ActivityManagerService 创建新的应用程序进程的请求;

参考

【 Android 10 系统启动 】系列 – Zygote(进程孵化器)

猜你喜欢

转载自blog.csdn.net/xingyu19911016/article/details/127686947