Android深造之Zygote, System Server 启动分析

【转载+自己的一些改动】图解Android – Zygote, System Server 启动分析
Init 是所有Linux程序的起点,而Zygote于Android,正如它的英文意思,是所有java程序的’孵化池’(玩过星际虫族的兄弟都晓得的)。用ps 输出可以看到

>adb shell ps | grep -E 'init|926'
 root      1     0     656    372   00000000 0805d546 S /init
 root      926   1     685724 43832 ffffffff b76801e0 S zygote
 system    1018  926   795924 62720 ffffffff b767fff6 S system_server
 u0_a6     1241  926   717704 39252 ffffffff b76819eb S com.android.systemui
 u0_a37    1325  926   698280 29024 ffffffff b76819eb S com.android.inputmethod.latin
 radio     1349  926   711284 30116 ffffffff b76819eb S com.android.phone
 u0_a7     1357  926   720792 41444 ffffffff b76819eb S com.android.launcher
 u0_a5     1523  926   703576 26416 ffffffff b76819eb S com.android.providers.calendar
 u0_a25    1672  926   693716 21328 ffffffff b76819eb S com.android.musicfx
 u0_a17    2040  926   716888 33992 ffffffff b76819eb S android.process.acore
 u0_a21    2436  926   716060 23904 ffffffff b76819eb S com.android.calendar

init 是 zygote的父进程, 而system_server和其他所有的com.xxx结尾的应用程序都是从zygote fork 而来。本文将图过图表(辅予少量的代码)的方式来描述Zygote,system server 以及android application的启动过程。
废话少说,奉上两张大图开启我们的Zygote之旅。 第一张图是Zygote相关的所有类的结构图,另一张是Zygote启动的流程图。
《Android深造之Zygote, System Server 启动分析》
按图索骥,我们按照图一中的序号一一分解Zygote的启动过程。

1. App_Process

  • APP_Process: 启动zygote和其他Java程序的应用程序, 代码位于frameworks/base/cmds/app_process/app_main.cpp, 在init.rc 里面指定。
    #init.rc
    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

    代码如下

    #app_main.cpp
    ...
    else if (strcmp(arg, "--zygote") == 0) {
        zygote = true;
        niceName = "zygote";
    } else if (strcmp(arg, "--start-system-server") == 0) {
        startSystemServer = true;
    } else if (strcmp(arg, "--application") == 0) {
        application = true;
    }
    ...
    if (zygote) {
       runtime.start("com.android.internal.os.ZygoteInit",
            startSystemServer ? "start-system-server" : "");
    } else if (className) {
       // Remainder of args get passed to startup class main()
       runtime.mClassName = className;
       ...
       runtime.start("com.android.internal.os.RuntimeInit",
             application ? "application" : "tool");
    } else {
    }

    可以看到,app_process 里面定义了三种应用程序类型:
    1.  Zygote:  com.android.internal.os.ZygoteInit
    2.  System Server, 不单独启动,而是由Zygote启动
    3.  其他指定类名的Java 程序,比如说常用的 am. /system/bin/am 其实是一个shell程序,它的真正实现是

    exec app_process $base/bin com.android.commands.am.Am "$@"

    这些Java的应用都是通过 AppRuntime.start(className)开始的。从第一张大图可以看出,其实AppRuntime是AndroidRuntime的子类,它主要实现了几个回调函数,而start()方法是实现在AndroidRuntime这个方法类里,实现代码如下:

    #app_main.cpp
    class AppRuntime : public AndroidRuntime
    {
    public:
        AppRuntime()
            : mParentDir(NULL)
            , mClassName(NULL)
            , mClass(NULL)
            , mArgC(0)
            , mArgV(NULL)
        {
        }
    #if 0
        // this appears to be unused
        const char* getParentDir() const
        {
            return mParentDir;
        }
    #endif
        const char* getClassName() const
        {
            return mClassName;
        }
        virtual void onVmCreated(JNIEnv* env)
        {
            if (mClassName == NULL) {
                return; // Zygote. Nothing to do here.
            }
            /*
             * This is a little awkward because the JNI FindClass call uses the
             * class loader associated with the native method we're executing in.
             * If called in onStarted (from RuntimeInit.finishInit because we're
             * launching "am", for example), FindClass would see that we're calling
             * from a boot class' native method, and so wouldn't look for the class
             * we're trying to look up in CLASSPATH. Unfortunately it needs to,
             * because the "am" classes are not boot classes.
             *
             * The easiest fix is to call FindClass here, early on before we start
             * executing boot class Java code and thereby deny ourselves access to
             * non-boot classes.
             */
            char* slashClassName = toSlashClassName(mClassName);
            mClass = env->FindClass(slashClassName);
            if (mClass == NULL) {
                LOGE("ERROR: could not find class '%s'\n", mClassName);
            }
            free(slashClassName);
            mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
        }
        virtual void onStarted()
        {
            sp<ProcessState> proc = ProcessState::self();
            if (proc->supportsProcesses()) {
                LOGV("App process: starting thread pool.\n");
                proc->startThreadPool();
            }
            AndroidRuntime* ar = AndroidRuntime::getRuntime();
            ar->callMain(mClassName, mClass, mArgC, mArgV);
            if (ProcessState::self()->supportsProcesses()) {
                IPCThreadState::self()->stopProcess();
            }
        }
        virtual void onZygoteInit()
        {
            sp<ProcessState> proc = ProcessState::self();
            if (proc->supportsProcesses()) {
                LOGV("App process: starting thread pool.\n");
                proc->startThreadPool();
            }
        }
        virtual void onExit(int code)
        {
            if (mClassName == NULL) {
                // if zygote
                if (ProcessState::self()->supportsProcesses()) {
                    IPCThreadState::self()->stopProcess();
                }
            }
            AndroidRuntime::onExit(code);
        }
        const char* mParentDir;
        const char* mClassName;
        jclass mClass;
        int mArgC;
        const char* const* mArgV;
    };
    }

     

  • 什么是AnroidRuntime? 我们接下来马上开始。需要注意的是Zygote并不是Init启动的第一个程序,从PID可以看出来,在它之前,一下Native实现的重要System Daemon (后台进程)可能先起来,比如 ServiceManager (service的DNS服务).

2. AndroidRuntime

首先,什么是Runtime ?看看Wiki给的几种解释:

我倾向这里指的是后者,看看更进一步的解释:
In computer programming, a runtime library is the API used by a compiler to invoke some of the behaviors of a runtime system. The runtime system implements the execution model and other fundamental behaviors of a programming language. The compiler inserts calls to the runtime library into the executable binary. During execution (run time) of that computer program, execution of those calls to the runtime library cause communication between the application and theruntime system. This often includes functions for input and output, or for memory management.
归纳起来的意思就是,Runtime 是支撑程序运行的基础库,它是与语言绑定在一起的。比如:

  • C Runtime:就是C standard lib, 也就是我们常说的libc。(有意思的是, Wiki会自动将“C runtime” 重定向到 “C Standard Library”).
  • Java Runtime: 同样,Wiki将其重定向到” Java Virtual Machine”, 这里当然包括Java 的支撑类库(.jar).
  • AndroidRuntime:  显而易见,就是为Android应用运行所需的运行时环境。这个环境包括以下东东:
    • Dalvik VM: Android的Java VM, 解释运行Dex格式Java程序。每个进程运行一个虚拟机(什么叫运行虚拟机?说白了,就是一些C代码,不停的去解释Dex格式的二进制码(Bytecode),把它们转成机器码(Machine code),然后执行,当然,现在大多数的Java 虚拟机都支持JIT,也就是说,bytecode可能在运行前就已经被转换成机器码,从而大大提高了性能。过去一个普遍的认识是Java 程序比C,C++等静态编译的语言慢,但随着JIT的介入和发展,这个已经完全是过去时了,JIT的动态性运行允许虚拟机根据运行时环境,优化机器码的生成,在某些情况下,Java甚至可以比C/C++跑得更快,同时又兼具平台无关的特性,这也是为什么Java如今如此流行的原因之一吧)。
    • Android的Java 类库, 大部分来自于 Apache Hamony, 开源的Java API 实现,如 java.lang, java.util, java.net. 但去除了AWT, Swing 等部件。
    • JNI: C和Java互调的接口。
    • Libc: Android也有很多C代码,自然少不了libc,注意的是,Android的libc叫 bionic C.

OK, 那就首先看看AndroidRuntime是怎么搭建起来的吧
《Android深造之Zygote, System Server 启动分析》
上图给出了Zygote启动的大概流程,入口是AndroidRuntime.start(),源码如下:

#AndroidRuntime.cpp
/*
 * 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".
 *
 * Passes the main function two arguments, the class name and the specified
 * options string.
 */
void AndroidRuntime::start(const char* className, const char* options)
{
    //省略代码----
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
    /* start the virtual machine */
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }
    onVmCreated(env);
    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    //省略代码----
}

根据传入参数的不同可以有两种启动方式,一个是 “com.android.internal.os.RuntimeInit”, 另一个是 “com.android.internal.os.ZygoteInit”, 对应RuntimeInit 和 ZygoteInit 两个类, 图中用绿色和粉红色分别表示。这两个类的主要区别在于Java端,可以明显看出,ZygoteInit 相比 RuntimeInit 多做了很多事情,比如说 “preload”, “gc” 等等。但是在Native端,他们都做了相同的事, startVM() 和 startReg(), 让我们先从这里开始吧。

  • startVM()

    • #AndroidRuntime.cpp 启动并初始化JVM
      //https://android.googlesource.com/platform/frameworks/base.git/+/android-4.2.2_r1/core/jni/AndroidRuntime.cpp
      int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
      {
          property_get("dalvik.vm.checkjni", propBuf, "");
          ...
          initArgs.version = JNI_VERSION_1_4;
          ...
          //上面的代码功能是初始化一系列的JVM参数
          //创建VM并返回JavaVM 和 JniEnv,pEnv对应于当前线程。一个进程对应一个JVM,一个线程对应一个JniEnv,而且不能跨线程使用
          if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
              ALOGE("JNI_CreateJavaVM failed\n");
              goto bail;
          }
          ...
      }
      jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
          memset(&gDvm, 0, sizeof(gDvm)); /* 这里才是真正的VM结构体*/
          JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));
          pVM->funcTable = &gInvokeInterface; //初始化函数指针
          pVM->envList = NULL;
          ...
          gDvmJni.jniVm = (JavaVM*) pVM; //native代码接触的JavaVM原来只是JniVm而已
          JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL); //创建JNIEnv,因为接下来的虚拟机初始化需要访问C/C++实现
          /* 开始初始化. */
          gDvm.initializing = true;
          std::string status =
                  dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
          gDvm.initializing = false;
          dvmChangeStatus(NULL, THREAD_NATIVE);
          *p_env = (JNIEnv*) pEnv;
          *p_vm = (JavaVM*) pVM;
          return JNI_OK;

      继续

      std::string dvmStartup(int argc, const char* const argv[],
              bool ignoreUnrecognized, JNIEnv* pEnv)
      {
          /*
           * 检查输入并准备初始化参数
           */
          int cc = processOptions(argc, argv, ignoreUnrecognized);
          ...
          /* 真正初始化开始,初始化各个内部模块,并创建一系列线程*/
          if (!dvmAllocTrackerStartup()) {
              return "dvmAllocTrackerStartup failed";
          }
          //。。。。
          return "";
      }

      Java虚拟机的启动有太多的细节在这里无法展开,这里我们只需要知道它做了以下一些事情:
      1.  从property读取一系列启动参数。
      2.  创建和初始化结构体全局对象(每个进程)gDVM,及对应与JavaVM和JNIEnv的内部结构体 JavaVMExt, JNIEnvExt.
      3.  初始化java虚拟机,并创建虚拟机线程。”ps -t”,你可以发现每个Android应用都有以下几个线程

      u0_a46    1284  1281  714900 57896 20    0     0     0     fg  ffffffff 00000000 S GC                 //垃圾回收
       u0_a46    1285  1281  714900 57896 20    0     0     0     fg  ffffffff 00000000 S Signal Catcher
       u0_a46    1286  1281  714900 57896 20    0     0     0     fg  ffffffff 00000000 S JDWP               //Java 调试
       u0_a46    1287  1281  714900 57896 20    0     0     0     fg  ffffffff 00000000 S Compiler           //JIT
       u0_a46    1288  1281  714900 57896 20    0     0     0     fg  ffffffff 00000000 S ReferenceQueueD
       u0_a46    1289  1281  714900 57896 20    0     0     0     fg  ffffffff 00000000 S FinalizerDaemon    //Finalizer监护
       u0_a46    1290  1281  714900 57896 20    0     0     0     fg  ffffffff 00000000 S FinalizerWatchd    //

      4. 注册系统的JNI,Java程序通过这些JNI接口来访问底层的资源。

          loadJniLibrary("javacore");
          loadJniLibrary("nativehelper");

      5. 为Zygote的启动做最后的准备,包括设置SID/UID, 以及mount 文件系统。
      6. 返回JavaVM 给Native代码,这样它就可以向上访问Java的接口。
      除了系统的JNI接口(”javacore”, “nativehelper”), android framework 还有大量的Native实现,Android将所有这些接口一次性的通过start_reg()来完成,

  • startReg()

    /*
     * 在VM中注册android原生函数。
     */
    /*static*/ int AndroidRuntime::startReg(JNIEnv* env)
    {
        /*
         * 将以后需要创建的线程加到JavaVM中(这样有利于JNI调用)
         */
        androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
        ALOGV("--- registering native functions ---\n");
        /*
         * Every "register" function calls one or more things that return
         * a local reference (e.g. FindClass).  Because we haven't really
         * started the VM yet, they're all getting stored in the base frame
         * and never released.  Use Push/Pop to manage the storage.
         */
        env->PushLocalFrame(200);
        if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
            env->PopLocalFrame(NULL);
            return -1;
        }
        env->PopLocalFrame(NULL);
        //createJavaThread("fubar", quickTest, (void*) "hello");
        return 0;
    }

    Android native层有两种Thread的创建方式:

    #threads.cpp
    status_t Thread::run(const char* name, int32_t priority, size_t stack)
    {
       ...
       if (mCanCallJava) {
            res = createThreadEtc(_threadLoop, this, name, priority, stack, &mThread);
       } else {
            res = androidCreateRawThreadEtc(_threadLoop,this, name, priority, stack, &mThread);
        }
       ...
    }

    它们的区别在是是否能够调用Java端函数,普通的thread就是对pthread_create的简单封装。

    int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
                                   void *userData,
                                   const char* threadName,
                                   int32_t threadPriority,
                                   size_t threadStackSize,
                                   android_thread_id_t *threadId)
    {
        ...
        int result = pthread_create(&thread, &attr,android_pthread_entry)entryFunction, userData);
        ...
    }

    能够访问Java端的thread需要跟JVM进行绑定,下面是具体的实现函数

    #AndroidRuntime.cpp
    int AndroidRuntime::javaCreateThreadEtc(
                                    android_thread_func_t entryFunction,
                                    void* userData,
                                    const char* threadName,
                                    int32_t threadPriority,
                                    size_t threadStackSize,
                                    android_thread_id_t* threadId)
    {
         args[0] = (void*) entryFunction; //将entryFunc 暂存在args[0]
         args[1] = userData;
         args[2] = (void*) strdup(threadName);
         result =AndroidCreateRawThreadEtc(AndroidRuntime::javaThreadShell, args, threadName, threadPriority, threadStackSize, threadId); //entryFunc变成javaThreadShell.
        return result;
    }
    int AndroidRuntime::javaThreadShell(void* args) {
        void* start = ((void**)args)[0];
        void* userData = ((void **)args)[1];
        char* name = (char*) ((void **)args)[2];        // we own this storage
        JNIEnv* env;
        /* 跟 VM 绑定 确保当前线程对VM可见*/
        if (javaAttachThread(name, &env) != JNI_OK)
            return -1;
        /* 运行真正的'entryFunc' */
        result = (*(android_thread_func_t)start)(userData);
        /* unhook us */
        javaDetachThread();
        ...
        return result;
    }

    attachVM() 到底做什么事情? 篇幅有限无法展开,这里只需要知道这么几点:

    • 一个进程里有一个Java 虚拟机,Java 虚拟机内部有很多线程,如上面列到的 GC, FinalizeDaemon, 以及用户创建的线程等等.
    • 每个Java线程都维护一个JNIEnvExt对象,里面存放一个指向DVM 内部Thread对象的指针,也就是说,所有从native到Java端的调用,都会引用到这个对象。
    • 所有通过JVM创建的线程都会在VM内部记录在案,但是当前,我们还没有进入Java世界,本地创建的线程VM自然就不知道,因此我们需要通过attach来通知VM来创建相应的内部数据结构。

看看下面代码,你就知道,其实Attach()做的一件重要的事情就是 创建thread和JNIEnvExt.

bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon)
{
    Thread* self = NULL;
    ...
    self = allocThread(gDvm.stackSize);
    ...
    self->jniEnv = dvmCreateJNIEnv(self);
    ...
    gDvm.threadList->next = self;
    ...
    threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT);
    vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
    ...
    self->threadObj = threadObj;
    ...
}

完了,就开始注册本地的JNI接口函数了- register_jni_procs(), 这个函数其实就是对一个全局数组gRegJni[] 进行遍历调用,这个数组展开可以得到以下的结果

static const RegJNIRec gRegJNI[] = {
   {register_android_debug_JNITest},
   {register_com_android_internal_os_RuntimeInit}.
    ...
}

每个 register_xxx是一个函数指针
RegisterNativeMethods 在VM内部到底发生了什么?

/*
 * Register native methods using JNI.
 */
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}

内部调用了jniRegisterNativeMethods方法,起作用是:

/*
 * Register one or more native methods with a particular class.
 * "className" looks like "java/lang/String". Aborts on failure.
 * TODO: fix all callers and change the return type to void.
 */

简单翻译后就是:注册指定的class
好了,经过了千辛万苦,Android的运行时环境都已经准备就绪了,让我们再回顾一下AndroidRuntime的初始化都做了哪些工作,

  1. 创建了Dalvik VM.
  2. 获取Native 访问Java的两个接口对象,JavaVM 和 JNIENV。
  3. 注册了一批 (见gRegJni[]) native接口给VM。

这些操作都是相对耗时的工作,如果每个进程都做同样的工作势必会影响到启动速度,这也是为什么我们需要通过Zygote来创建Android 应用,因为通过Linux fork 的 copy_on_write的机制,子进程可以将这些初始化好的内存空间直接映射到自己的进程空间里,不在需要做重复的工作,从而提高了应用启动的速度。
可以是,Android系统只需要基本的运行时环境就够了吗? 答案显然是No。AndriodRuntime 只是提供了语言层面的基础支持,在一个多任务,多用户的图形操作系统上快速的孵化和运行应用程序,我们需要更多。这就是Zygote,这就是为什么在图2中,ZygoteInit会比RuntimeInit做更多的事情。那接下来,让我们真正进入Zygote的世界。
当VM准备就绪,就可以运行Java代码了,系统也将在此第一次进入Java世界,还记得app_main.cpp里面调到的 Runtime.start()的参数吗, 那就是我们要运行的Java类。Android支持两个类做为起点,一个是‘com.android.internal.os.ZygoteInit‘, 另一个是’com.android.internal.os.RuntimeInit’。

#app_main.cpp
if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        // 。。。。
        runtime.start("com.android.internal.os.RuntimeInit",
                application ? "application" : "tool");
    }

先看一下RuntimeInit的main方法:

3. RuntimeInit

注意:Java部分源码皆基于:Android-28

    public static final void main(String[] argv) {
        enableDdms();
        if (argv.length == 2 && argv[1].equals("application")) {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
            redirectLogStreams();
        } else {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
        }
        commonInit();
        /*
         * Now that we're running in interpreted code, call back into native code
         * to run the system.
         */
        nativeFinishInit();
        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
    }
  • redirectLogStreams(): 将System.out 和 System.err 输出重定向到Android 的Log系统(定义在 android.util.Log).
        public static void redirectLogStreams() {
            System.out.close();
            System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
            System.err.close();
            System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
        }
  • commonInit(): 初始化了一下系统属性,其中最重要的一点就是设置了一个未捕捉异常的handler,当代码有任何未知异常,就会执行它,调试过Android代码的同学经常看到的”*** FATAL EXCEPTION IN SYSTEM PROCESS” 打印就出自这里:
    Runtime_init.java
    ...
    Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
    ...
    private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
            public void uncaughtException(Thread t, Throwable e) {
                try {
                    // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
                    if (mCrashing) return;
                    mCrashing = true;
                    if (mApplicationObject == null) {
                        Slog.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
                    } else {
                        Slog.e(TAG, "FATAL EXCEPTION: " + t.getName(), e);
                    }
                    ActivityManagerNative.getDefault().handleApplicationCrash(
                            mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
                } catch (Throwable t2) {
                  ...
                } finally {
                    Process.killProcess(Process.myPid());
                    System.exit(10);
                }
            }
        }

    RuntimeInit 的nativeFinishInit()在AndroidRuntime.cpp中进行了实现:

    /*
     * Code written in the Java Programming Language calls here from main().
     */
    static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
    {
        gCurRuntime->onStarted();
    }

    调用了当前Runtime里面的onStarted方法,上面我们讲到AppRuntime在app_main.cpp中实现了此方法。里面会调用Java类的main() 函数,然后结束进程退出。

    virtual void onStarted()
         {
             sp<ProcessState> proc = ProcessState::self();
             proc->startThreadPool();
             AndroidRuntime* ar = AndroidRuntime::getRuntime();
             ar->callMain(mClassName, mClass, mArgC, mArgV);
             IPCThreadState::self()->stopProcess();
         }

除了在Main方法是实现的逻辑之外,RunTimeInit还实现了提供给ZygoteInit调用方法:
applicationInit(int targetSdkVersion, String[] argv,ClassLoader classLoader)
这个方法主要用来是现在在fork出SystemtServer进程时,利用反射机制将需要的class加载到内存中,并且调用该class的main方法。稍后我们做详细介绍。

4.ZygotInit

首先看一下main方法:

    public static void main(String argv[]) {
        ZygoteServer zygoteServer = new ZygoteServer();//新建Zygote服务器端
        //...
        final Runnable caller;
        try {
            // Report Zygote start time to tron unless it is a runtime restart
            //....
            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            for (int i = 1; i < argv.length; i++) {
                //还记得app_main.cpp中传的start-system-server参数吗,在这里总有用到了
                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]);
                }
            }
            zygoteServer.registerServerSocketFromEnv(socketName);//注册Socket
            // 在有些情况下我们需要在第一个fork之前进行预加载资源
            if (!enableLazyPreload) {
                //...
                preload(bootTimingsTraceLog);
                //...
            } else {
                Zygote.resetNicePriority();
            }
            //主动进行一次资源GC
            gcAndFinalize();
            //...
            if (startSystemServer) {
                //生成SystemtServer进程
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
                // {@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);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            zygoteServer.closeServerSocket();
        }
        // We're in the child process and have exited the select loop. Proceed to execute the
        // command.
        if (caller != null) {
            caller.run();
        }
    }

然后看一下Main方法中调用到的几个主要方法:

  1. preload

    • preload() 的作用就是提前将需要的资源加载到VM中,比如class、resource等
          static void preload(TimingsTraceLog bootTimingsTraceLog) {
              Log.d(TAG, "begin preload");
              //加载指定的类到内存并且初始化,使用的Class.forName(class, true, null);方式
              preloadClasses();
              //加载Android通用的资源,比如drawable、color...
              preloadResources();
              //...
              nativePreloadAppProcessHALs();
              //加载OpenGL...
              preloadOpenGL();
              //加载共用的Library
              preloadSharedLibraries();
              //加载Text资源,字体等
              preloadTextResources();
              // 为了内存共享,WebViewFactory进行任何初始化都要在Zygote进程中
              WebViewFactory.prepareWebViewInZygote();
              //...
              sPreloadComplete = true;
          }

      preloadClassess 将framework.jar里的preloaded-classes 定义的所有class load到内存里,preloaded-classes 编译Android后可以在framework/base下找到。而preloadResources 将系统的Resource(不是在用户apk里定义的resource)load到内存。
      资源preload到Zygoted的进程地址空间,所有fork的子进程将共享这份空间而无需重新load, 这大大减少了应用程序的启动时间,但反过来增加了系统的启动时间。通过对preload 类和资源数目进行调整可以加快系统启动。Preload也是Android启动最耗时的部分之一。

  2.  gcAndFinalize

        /**
         * Runs several special GCs to try to clean up a few generations of
         * softly- and final-reachable objects, along with any other garbage.
         * This is only useful just before a fork().
         */
        /*package*/ static void gcAndFinalize() {
            final VMRuntime runtime = VMRuntime.getRuntime();
            /* runFinalizationSync() lets finalizers be called in Zygote,
             * which doesn't have a HeapWorker thread.
             */
            System.gc();
            runtime.runFinalizationSync();
            System.gc();
        }

    gc()调用只是通知VM进行垃圾回收,是否回收,什么时候回收全由VM内部算法决定。GC的回收有一个复杂的状态机控制,通过多次调用,可以使得尽可能多的资源得到回收。gc()必须在fork之前完成(接下来的StartSystemServer就会有fork操作),这样将来被复制出来的子进程才能有尽可能少的垃圾内存没有释放。

  3. forkSystemServer

        private static Runnable forkSystemServer(String abiList, String socketName,
                ZygoteServer zygoteServer) {
            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
            );
            //...
            /* 启动SystemServer的命令行,部分参数写死 */
            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);//会设置InvokeWith参数,这个参数在接下来的初始化逻辑中会有调用
                /* 创建 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 */
            if (pid == 0) {//如果是第一次创建的话pid==0
                if (hasSecondZygote(abiList)) {
                    waitForSecondaryZygote(socketName);
                }
                zygoteServer.closeServerSocket();
                return handleSystemServerProcess(parsedArgs);
            }
            return null;
        }

    ZygoteInit.forkSystemServer() 方法fork 出一个新的进程,这个进程就是SystemServer进程。fork出来的子进程在handleSystemServerProcess 里开始初始化工作,主要工作分为:

    1. prepareSystemServerProfile()方法中将SYSTEMSERVERCLASSPATH中的AppInfo加载到VM中。
    2. 判断fork args中是否有invokWith参数,如果有则进行WrapperInit.execApplication(不进行深入讲解了)。如果没有则调用:
          public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
              if (RuntimeInit.DEBUG) {
                  Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
              }
              Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
              RuntimeInit.redirectLogStreams();
              RuntimeInit.commonInit();
              ZygoteInit.nativeZygoteInit();
              return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
          }

      是不是很熟悉,除了调用了RuntimeInit.redirectLogSteams和RuntimeInit.commonInit外,还调用了RuntimeInit.applicationInit,那applicationInit到底是做什么的呢?

          protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
                  ClassLoader classLoader) {
              //省略代码
              return findStaticMain(args.startClass, args.startArgs, classLoader);
          }

      很明显,其实就是调用了findStaticMain来运行args的startClass的main方法:

          protected static Runnable findStaticMain(String className, String[] argv,
                  ClassLoader classLoader) {
              Class<?> cl;
                  cl = Class.forName(className, true, classLoader);
              Method m;
                  m = cl.getMethod("main", new Class[] { String[].class });
              //省略部分代码
              return new MethodAndArgsCaller(m, argv);
          }

      很明显这是一个耗时操作所以使用线程来完成:

          static class MethodAndArgsCaller implements Runnable {
              /** method to call */
              private final Method mMethod;
              /** argument array */
              private final String[] mArgs;
              public MethodAndArgsCaller(Method method, String[] args) {
                  mMethod = method;
                  mArgs = args;
              }
              public void run() {
                  try {
                      mMethod.invoke(null, new Object[] { mArgs });
                  } catch (IllegalAccessException ex) {
                      //
                  } catch (InvocationTargetException ex) {
                      //
                  }
              }
          }

       

4. System Server 启动流程

System Server 是Zygote fork 的第一个Java 进程, 这个进程非常重要,因为他们有很多的系统线程,提供所有核心的系统服务,我们可以用 ‘ps -t |grep <system server pid>’来看看都有哪些线程,排除前面列出的几个Java 虚拟机线程,还有

system    1176  1163  774376 51144 00000000 b76c4ab6 S SensorService
system    1177  1163  774376 51144 00000000 b76c49eb S er.ServerThread
system    1178  1163  774376 51144 00000000 b76c49eb S UI
system    1179  1163  774376 51144 00000000 b76c49eb S WindowManager
system    1180  1163  774376 51144 00000000 b76c49eb S ActivityManager
system    1182  1163  774376 51144 00000000 b76c4d69 S ProcessStats
system    1183  1163  774376 51144 00000000 b76c2bb6 S FileObserver
system    1184  1163  774376 51144 00000000 b76c49eb S PackageManager
system    1185  1163  774376 51144 00000000 b76c49eb S AccountManagerS
system    1187  1163  774376 51144 00000000 b76c49eb S PackageMonitor
system    1188  1163  774376 51144 00000000 b76c4ab6 S UEventObserver
system    1189  1163  774376 51144 00000000 b76c4d69 S BatteryUpdateTi
system    1190  1163  774376 51144 00000000 b76c49eb S PowerManagerSer
system    1191  1163  774376 51144 00000000 b76c2ff6 S AlarmManager
system    1192  1163  774376 51144 00000000 b76c4d69 S SoundPool
system    1193  1163  774376 51144 00000000 b76c4d69 S SoundPoolThread
system    1194  1163  774376 51144 00000000 b76c49eb S InputDispatcher
system    1195  1163  774376 51144 00000000 b76c49eb S InputReader
system    1196  1163  774376 51144 00000000 b76c49eb S BluetoothManage
system    1197  1163  774376 51144 00000000 b76c49eb S MountService
system    1198  1163  774376 51144 00000000 b76c4483 S VoldConnector
system    1199  1163  774376 51144 00000000 b76c49eb S CallbackHandler
system    1201  1163  774376 51144 00000000 b76c4483 S NetdConnector
system    1202  1163  774376 51144 00000000 b76c49eb S CallbackHandler
system    1203  1163  774376 51144 00000000 b76c49eb S NetworkStats
system    1204  1163  774376 51144 00000000 b76c49eb S NetworkPolicy
system    1205  1163  774376 51144 00000000 b76c49eb S WifiP2pService
system    1206  1163  774376 51144 00000000 b76c49eb S WifiStateMachin
system    1207  1163  774376 51144 00000000 b76c49eb S WifiService
system    1208  1163  774376 51144 00000000 b76c49eb S ConnectivitySer
system    1214  1163  774376 51144 00000000 b76c49eb S WifiManager
system    1215  1163  774376 51144 00000000 b76c49eb S Tethering
system    1216  1163  774376 51144 00000000 b76c49eb S CaptivePortalTr
system    1217  1163  774376 51144 00000000 b76c49eb S WifiWatchdogSta
system    1218  1163  774376 51144 00000000 b76c49eb S NsdService
system    1219  1163  774376 51144 00000000 b76c4483 S mDnsConnector
system    1220  1163  774376 51144 00000000 b76c49eb S CallbackHandler
system    1227  1163  774376 51144 00000000 b76c49eb S SyncHandlerThre
system    1228  1163  774376 51144 00000000 b76c49eb S AudioService
system    1229  1163  774376 51144 00000000 b76c49eb S backup
system    1233  1163  774376 51144 00000000 b76c49eb S AppWidgetServic
system    1240  1163  774376 51144 00000000 b76c4d69 S AsyncTask #1
system    1244  1163  774376 51144 00000000 b76c42a3 S Thread-64
system    1284  1163  774376 51144 00000000 b76c4d69 S AsyncTask #2
system    1316  1163  774376 51144 00000000 b76c2bb6 S UsbService host
system    1319  1163  774376 51144 00000000 b76c4d69 S watchdog
system    1330  1163  774376 51144 00000000 b76c49eb S LocationManager
system    1336  1163  774376 51144 00000000 b76c2ff6 S Binder_3
system    1348  1163  774376 51144 00000000 b76c49eb S CountryDetector
system    1354  1163  774376 51144 00000000 b76c49eb S NetworkTimeUpda
system    1360  1163  774376 51144 00000000 b76c2ff6 S Binder_4
system    1391  1163  774376 51144 00000000 b76c2ff6 S Binder_5
system    1395  1163  774376 51144 00000000 b76c2ff6 S Binder_6
system    1397  1163  774376 51144 00000000 b76c2ff6 S Binder_7
system    1516  1163  774376 51144 00000000 b76c4d69 S SoundPool
system    1517  1163  774376 51144 00000000 b76c4d69 S SoundPoolThread
system    1692  1163  774376 51144 00000000 b76c4d69 S AsyncTask #3
system    1694  1163  774376 51144 00000000 b76c4d69 S AsyncTask #4
system    1695  1163  774376 51144 00000000 b76c4d69 S AsyncTask #5
system    1791  1163  774376 51144 00000000 b76c4d69 S pool-1-thread-1
system    2758  1163  774376 51144 00000000 b76c4d69 S AudioTrack
system    2829  1163  774376 51144 00000000 b76c49eb S KeyguardWidgetP

看到大名鼎鼎的WindowManager, ActivityManager了吗?对了,它们都是运行在system_server的进程里。还有很多“Binder-x”的线程,它们是各个Service为了响应应用程序远程调用请求而创建的。除此之外,还有很多内部的线程,比如 ”UI thread”, “InputReader”, “InputDispatch” 等等,我们将在后续的文章里将这些模块具体分析。本文,我们只关心System Server是如何创建起来的。
SystemServer的main() 函数。

    /**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }

记下来我分成4部分详细分析SystemServer run方法的初始化流程:

  1. 出事必要的SystemServer环境参数,比如系统时间、默认时区、语言、load一些Library等等,这里就不在贴代码了
  2. 初始化Looper,惊不惊喜,意不意外,我们在祝线程中使用到的looper就是在SystemServer中进行初始化的
  3. 初始化Context,只有初始化一个Context才能进行启动Service等操作,这里看一下源码:
        private void createSystemContext() {
            ActivityThread activityThread = ActivityThread.systemMain();
            mSystemContext = activityThread.getSystemContext();
            mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);
            final Context systemUiContext = activityThread.getSystemUiContext();
            systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
        }

    看到没有ActivityThread就是这个时候生成的,继续看ActivityThread中如何生成Context:

        public ContextImpl getSystemContext() {
            synchronized (this) {
                if (mSystemContext == null) {
                    mSystemContext = ContextImpl.createSystemContext(this);
                }
                return mSystemContext;
            }
        }

    ContextImpl是Context类的具体实现,里面封装完成了生成几种常用的createContext的方法:

        static ContextImpl createSystemContext(ActivityThread mainThread) {
            LoadedApk packageInfo = new LoadedApk(mainThread);
            //省略代码
            return context;
        }
        static ContextImpl createSystemUiContext(ContextImpl systemContext) {
            final LoadedApk packageInfo = systemContext.mPackageInfo;
            //省略代码
            return context;
        }
        static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
            if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
            //省略代码
            return context;
        }
        static ContextImpl createActivityContext(ActivityThread mainThread,
                LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
                Configuration overrideConfiguration) {
            //省略代码
            return context;
        }
  4. 初始化SystemServiceManager,用来管理启动service,SystemServiceManager中封装了启动Service的startService方法
  5. 启动系统必要的Service,启动service的流程又分成三步走:
    // Start services.
            try {
                traceBeginAndSlog("StartServices");
                startBootstrapServices();
                startCoreServices();
                startOtherServices();
                SystemServerInitThreadPool.shutdown();
            } catch (Throwable ex) {
                //
            } finally {
                traceEnd();
            }
    1. 启动BootstrapServices,就是系统必须需要的服务,这些服务直接耦合性很高,所以干脆就放在一个方法里面一起启动,比如PowerManagerService、RecoverySystemService、DisplayManagerService、ActivityManagerService等等
    2. 启动以基本的核心Service,很简单,只有三个BatteryService、UsageStatsService、WebViewUpdateService
    3. 启动其它需要用到的Service,比如NetworkScoreService、AlarmManagerService

5. 善后工作

是不是到此之后,Zygote的工作变得很轻松了,可以宜养天年了?可惜现代社会,哪个父母把孩子养大就可以撒手不管了?尤其是像Sytem Server 这样肩负社会重任的大儿子,出问题了父母还是要帮一把的。这里,Zygote会默默的在后台凝视这自己的大儿子,一旦发现System Server 挂掉了,将其回收,然后将自己杀掉,重新开始新的一生, 可怜天下父母心啊。这段实现在代码 :dalvik/vm/native/dalvik_system_zygote.cpp 中,

static void Dalvik_dalvik_system_Zygote_forkSystemServer(
        const u4* args, JValue* pResult){
    ...
    pid_t pid;
    pid = forkAndSpecializeCommon(args, true);
    ...
    if (pid > 0) {
        int status;
        gDvm.systemServerPid = pid;
        /* WNOHANG 会让waitpid 立即返回,这里只是为了预防上面的赋值语句没有完成之前SystemServer就crash 了*/
        if (waitpid(pid, &status, WNOHANG) == pid) {
            ALOGE("System server process %d has died. Restarting Zygote!", pid);
            kill(getpid(), SIGKILL);
        }
    }
    RETURN_INT(pid);
}
/* 真正的处理在这里 */
static void sigchldHandler(int s)
{
    ...
    pid_t pid;
    int status;
    ...
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        ...
        if (pid == gDvm.systemServerPid) {
            ...
            kill(getpid(), SIGKILL);
        }
    }
    ...
}
static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult)
{
    pid_t pid;
    ...
    setSignalHandler(); //signalHandler 在这里注册
    ...
    pid = fork();
    ...
    RETURN_INT(pid);
}

在Unix-like系统,父进程必须用 waitpid 等待子进程的退出,否则子进程将变成”Zombie” (僵尸)进程,不仅系统资源泄漏,而且系统将崩溃(没有system server,所有Android应用程序都无法运行)。但是waitpid() 是一个阻塞函数(WNOHANG参数除外),所以通常做法是在signal 处理函数里进行无阻塞的处理,因为每个子进程退出的时候,系统会发出 SIGCHID 信号。Zygote会把自己杀掉, 那父亲死了,所有的应用程序不就成为孤儿了? 不会,因为父进程被杀掉后系统会自动给所有的子进程发生SIGHUP信号,该信号的默认处理就是将杀掉自己退出当前进程。但是一些后台进程(Daemon)可以通过设置SIG_IGN参数来忽略这个信号,从而得以在后台继续运行。

总结

Zygote和System Server的启动过程终于介绍完了,让我们对着上面这张完整的类图再来重温一下这个过程吧。
1.  init 根据init.rc 运行 app_process, 并携带‘–zygote’ 和 ’–startSystemServer’ 参数。
2.  AndroidRuntime.cpp::start() 里将启动JavaVM,并且注册所有framework相关的系统JNI接口。
3.  第一次进入Java世界,运行ZygoteInit.java::main() 函数初始化Zygote. Zygote 并创建Socket的server 端。
4.  然后fork一个新的进程并在新进程里初始化SystemServer. Fork之前,Zygote是preload常用的Java类库,以及系统的resources,同时GC()清理内存空间,为子进程省去重复的工作。
5.  SystemServer 里将所有的系统Service初始化,包括ActivityManager 和 WindowManager, 他们是应用程序运行起来的前提。
6.  依次同时,Zygote监听服务端Socket,等待新的应用启动请求。
7.  ActivityManager ready 之后寻找系统的“Startup” Application, 将请求发给Zygote。
8.  Zygote收到请求后,fork出一个新的进程。
9.  Zygote监听并处理SystemServer 的 SIGCHID 信号,一旦System Server崩溃,立即将自己杀死。init会重启Zygote.

点赞

发表评论