Android 6.0 系统学习之 Zygote

一次看到 Zygote 时,不明白这个名字什么意思,查了一下发现是受精卵的意思。Zygote 是 Android 系统中启动应用程序与系统服务的服务。

学习 Zygote 这个服务,我想弄明白这些问题:

  1. Zygote 自己是由谁启动的?
  2. Zygote 都能启动哪些东西?
  3. Zygote 是如何启动其他程序、服务的?

现有资源

现有的关于 Zygote 的文章已有很多。但是纸上得来终觉浅,绝知此事要孤行。前辈分享的知识,吃透理解后,沉淀成自己的东西才算是自己的。

在这篇文章中,我主要学习了邓凡平的《Android深入浅出之Zygote》老罗的《Android系统进程Zygote启动过程的源代码分析》。感谢这些前辈的无私奉献,它们的努力,使我在后来的学习中减少了许多困难。

谁负责启动 Zygote?

既然 Zygote 负责启动 Android 系统的各个部分,我的第一个问题是它自己是如何自举的?

Android 以 Linux 为核心,这个问题交给了 Linux。init 程序 Linux 中负责载入 Linux 各个部分的程序,它在成功加载 Linux 系统后,按照 Android 启动脚本 init.rc 文件中的配置,加载 Zygote。

配置文件在 Android 6.0 代码中位于 system/core/rootdir/init.zygote32.rc,具体内容为:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

其中:

  1. 关键字 service 表示告诉 init 进程创建名为 zygote 的进程,所要执行的程序是 /system/bin/app_process,后面的都是传递的参数。
  2. 注意参数 --start-system-server,说明要启动 SystemServer
  3. socket zygote stream 660 root system 表示创建名为 zygote 的 socket。
  4. 后面的 onrestart 关键字表示 zygote 进程重启时所需执行的命令。

Zygote 的启动

从中我们可以得出结论: zygote 只是服务的名称,与此服务对应的程序是 app_process 程序,我们研究 zygote 的实现,就是要研究 app_process 程序。

app_process 程序

app_process 代码位于 frameworks/base/cmds/app_process/app_main.cpp,入口函数为 main。将 main 函数的主要逻辑抽象出来:

int main(int argc, char* const argv[])
{
    ...
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ...
    // 解析参数,设置 Flag...
    ...
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        ...
        return 10;
    }
}

main 的大体流程为:

  1. 创建一个 AppRuntime 实例 runtime
  2. 解析传入的命令行参数
  3. 判断调用哪一个 runtime.start,传入对应的参数

由于 init.zygote32.rc 中传入参数 -Xzygote /system/bin --zygote --start-system-server,因此将执行:

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

由此可知,app_process 没干什么主要的事情,只是跳转到 Java 类 com.android.internal.os.ZygoteInit,看来工作都在 ZygoteInit 中完成。

在跳到 ZygoteInit 之前,先来看看 app_process 中用到的 AppRuntime。

AndroidRuntime

start 方法来自于 AppRuntime 的父类 AndroidRuntime,代码位于 frameworks/base/core/jni/AndroidRuntime.cpp。来看 start 方法 (在 6.0 代码中位于 1007 行):

/*
 * Start the Android runtime.  This involves starting the virtual machine
 * and calling the "static void main(String[] args)" method in the class
 * named by "className".
 * 启动 Android 运行时。这包括启动虚拟机和调用 "className" 对应的类
 * 的 "static void main(String[] args)" 方法。
 *
 * Passes the main function two arguments, the class name and the specified
 * options string.
 * 将会向指定类的 main 函数传入两个参数,也就是 start 函数的 className 
 * 和 options
 */
 void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
 {
    ...
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    ...

    /*
     * 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);
    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 {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

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

AndroidRuntime 的 runtime 主要做了三件事:

首先是创建虚拟机:

/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
   return;
}
onVmCreated(env);

之后是调用 startReg 函数注册 JNI 方法:

/*
* Register android functions.
*/
if (startReg(env) < 0) {
   ALOGE("Unable to register all android natives\n");
   return;
}

最后构建参数、创建 JNI 类对象,获取 main 方法,并最终执行下面一行执行 main 入口:

env->CallStaticVoidMethod(startClass, startMeth, strArray);

这样我们就成功从 C 中启动了一个虚拟机,并加载了 Java 类 ZygoteInit,并进入到它的 main 方法中执行。

ZygoteInit

之后就来到了 ZygoteInit.java 的 main 方法,代码位于 frameworks/base/core/java/com/android/internal/os/ZygoteInit.java。来看其 main 方法:

public static void main(String argv[]) {
   try {
       ...
       registerZygoteSocket(socketName);
       ...
       if (startSystemServer) {
           startSystemServer(abiList, socketName);
       }
       ...
       runSelectLoop(abiList);
       ...
   } catch (...) { ... }
}

首先,调用 registerZygoteSocket 创建了一个名为 zygote 的 socket:

registerZygoteSocket(socketName);

之后启动 SystemServer 组件:

if (startSystemServer) {
 startSystemServer(abiList, socketName);
}

最后调用 runSelectLoopMode 进入一个死循环,等待接受 socket 上由 ActivityManagerService 发来的请求创建应用程序的请求:

runSelectLoop(abiList);

下面我们就对后两个步骤详细分析。

一、启动 SystemServer 组件

SystemServer 名为系统服务进程,负责启动 Android 系统的关键服务。来看看函数的主要实现:

private static boolean startSystemServer(String abiList, String socketName)
       throws MethodAndArgsCaller, RuntimeException {
    ...
    /* Request to fork the system server process */
    pid = Zygote.forkSystemServer(
        parsedArgs.uid, parsedArgs.gid,
        parsedArgs.gids,
        parsedArgs.debugFlags,
        null,
        parsedArgs.permittedCapabilities,
        parsedArgs.effectiveCapabilities);
    ...
    /* For child process */
    if (pid == 0) {
        ...
        handleSystemServerProcess(parsedArgs);
    }
    return true;
}

首先调用了 Zygote 的静态方法 forkSystemServer 来创建 SystemServer 进程。这也是我首次看到 Zygote 是如何孵♂化出东西来的。

我们进入 forkSystemServer 看看具体实现:

public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
       int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
   ...
   int pid = nativeForkSystemServer(
           uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
   ...
   return pid;
}

可见,这又调用了 nativeForkSystemServer,这是一个 jni 调用,对应的函数为 com_android_internal_os_Zygote_nativeForkSystemServer,位于:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp,来看看它的实现:

static jint com_android_internal_os_Zygote_nativeForkSystemServer(
        JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
        jint debug_flags, jobjectArray rlimits, jlong permittedCapabilities,
        jlong effectiveCapabilities) {
  pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
                                      debug_flags, rlimits,
                                      permittedCapabilities, effectiveCapabilities,
                                      MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
                                      NULL, NULL);
  ...
  return pid;
}

在这里调用了 ForkAndSpecializeCommon 函数,它是一个静态函数,用来 fork Zygote 并设置子进程。代码比较多,看看它的主要实现:

static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
                                     jint debug_flags, jobjectArray javaRlimits,
                                     jlong permittedCapabilities, jlong effectiveCapabilities,
                                     jint mount_external,
                                     jstring java_se_info, jstring java_se_name,
                                     bool is_system_server, jintArray fdsToClose,
                                     jstring instructionSet, jstring dataDir) {
  ...
  pid_t pid = fork();
  if (pid == 0) {
    ...
    // 对子线程进行了一大堆设置
    ...
  }
  return pid;
} 

首先,执行 fork() 创建了一个子进程。在子进程中进行了一大堆设置。

跳了这么一大堆,来梳理一下。启动 SystemServer,首先要创建一个新进程,可我们现在在 Java 中,需要会到 C 中进行 fork(),我们通过 forkSystemServer - com_android_internal_os_Zygote_nativeForkSystemServer - ForkAndSpecializeCommon 来实现 JNI 调用并成功 fork,这样新进程就有了。

让我们回到本节的一开始的 startSystemServer,现在子进程已经创建好了,接着往下看。在创建的子进程中,有一句 handleSystemServerProcess(parsedArgs),进入看看实现:

    /**
     * Finish remaining work for the newly forked system server process.
     */
    private static void handleSystemServerProcess(
            ZygoteConnection.Arguments parsedArgs)
            throws ZygoteInit.MethodAndArgsCaller {
    ...
    final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
    ...
    if (...) {
        ...
    } else {
      ClassLoader cl = null;
      if (systemServerClasspath != null) {
          cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
          Thread.currentThread().setContextClassLoader(cl);
      }

      /*
       * Pass the remaining arguments to SystemServer.
       */
      RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
    }

从中可以看出,我们从环境变量 SYSTEMSERVERCLASSPATH 拿到 SystemServer 的类名,之后载入进来,最后使用 RuntimeInit.zygoteInit 来运行,它来执行 SystemServer 的 main 方法。

RuntimeInit.zygoteInit 的具体实现可以简单看一下:

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
        throws ZygoteInit.MethodAndArgsCaller {
    ...
    commonInit();   // 基本设置(异常捕获、时区、HTTP User-Agent 等)
    nativeZygoteInit();
    applicationInit(targetSdkVersion, argv, classLoader); // 调用 Main 方法
}

其中 nativeZygoteInit() 是一个 jni 调用,位于 AndroidRuntime.cpp:

static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onZygoteInit();
}

onZygoteInit() 是 AppRuntime 中的方法,具体为:

virtual void onZygoteInit()
{
   sp<ProcessState> proc = ProcessState::self();
   ALOGV("App process: starting thread pool.\n");
   proc->startThreadPool();
}

可见是启动了一个线程池。nativeZygoteInit() 就分析到这里,由此可见,Zygote 启动 SystemServer 的过程就算完了,之后的,都是 SystemServer 内部的事情了。

二、runSelectLoop

下面看看 runSelectLoop 的实现。

/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
* 
* 运行 zygote 进程的 select 循环。接受新的连接,读取指令。
*
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
* 
* 如果有 main() 需要被执行,抛出 MethodAndArgsCaller 异常。
*/
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
   ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
   ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

   fds.add(sServerSocket.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 {
           Os.poll(pollFds, -1);
       } catch (...) {
           ...
       }
       for (int i = pollFds.length - 1; i >= 0; --i) {
           if ((pollFds[i].revents & POLLIN) == 0) {
               continue;
           }
           if (i == 0) {
               ZygoteConnection newPeer = acceptCommandPeer(abiList);
               peers.add(newPeer);
               fds.add(newPeer.getFileDesciptor());
           } else {
               boolean done = peers.get(i).runOnce();
               if (done) {
                   peers.remove(i);
                   fds.remove(i);
               }
           }
       }
   }
}

首先获取 socket 并加入到 fds 中:

fds.add(sServerSocket.getFileDescriptor());

之后执行:

Os.poll(pollFds, -1);

poll 会判断 pollFds 是否可读,若不可读则阻塞等待。

之后进入 for 循环:

for (int i = pollFds.length - 1; i >= 0; --i) {
    if ((pollFds[i].revents & POLLIN) == 0) {
        continue;
    }
    if (i == 0) {
        ZygoteConnection newPeer = acceptCommandPeer(abiList);
        peers.add(newPeer);
        fds.add(newPeer.getFileDesciptor());
    } else {
        boolean done = peers.get(i).runOnce();
        if (done) {
            peers.remove(i);
            fds.remove(i);
        }
    }
}

首先看是否已经在处理队列中,若已在,则处理下一个。之后看新加的(最后一个又是未处理,那就是新家的),那就解析参数加进去。最后,对于已经加入的,判断一下是否完成,完成了就从列表中删除。

注意,这些代码都套在 while(true) 死循环里,这个 poll 的过程会一直进行。

由此可见,Zygote 到这里就算启动完了,并进入了一个死循环,要想启动个东西,就从 socket 向它发指令,他就会孵♂化。

至此,我关于 Zygote 的疑问基本搞明白了。本文对于 Zygote 的机制挖掘并不很深,对于 Zygote 的底层操作也没有挖掘彻底。我想,对于了解来说,已经足够了。