Android 重学系列--系统启动到Activity

背景

为什么要写这个,是因为做android到今天,发现自己的基础不牢固,很多东西早年明白是怎么回事,下意识的知道怎么做,android这一块做久了过多的思考业务与设计,反而原理开始模模糊糊了。但是探究其原因,是过久没接触,成为了下意识行为。这是个不好的信号,也是一个不好的开始。最近又听闻在tx的哥们,以及面试大厂成功的哥们,一问起他们的面试题,以及工作情况,原以为自己的水平还算过的去,发现自己在腾讯连个t2等级的岗位都感觉很悬,真的是坐井观天。

于是今天我要端正态度,下定决心从头开始复习,为自己的基础打牢固,希望自己能够持之以恒,让自己走到更高的高度。那么就从我们最熟悉的四大组件的Activity开始吧。

从这次开始,我会将长博客分开几次来写。不会像之前的,一次性写完。主要是发现一口气写完数万字,可读性太差了。

正文

Activity的生命周期有七大生命周期:onCreate,onStart,onResume,onStop,onPause,onDestroy,onRestart.为什么这么划分,这么划分有什么好处,这些生命周期分别在做什么,在经历这些生命周期之前,Android做了又做了什么行为,这一切的一切值得让人探讨。

从系统启动到Activity。我们启动系统,到界面点击App的图标开始,研究Activity吧。

接下来,所有的源码都来自Android 7.0系统。

系统启动

从系统启动开始复习。我想起我当时的毕业设计,是第一次真正意义上的接触Linux系统。现在要把这些知识,从脑海中回忆整理出来。

Android系统的内核实际上是一个Linux内核,系统的一切启动,都是从Linux内核开始启动。

复习一下,Linux系统启动流程:
Linux启动流程大致分为2个步骤:
引导和启动。

引导:

分为BIOS上电子检(POST)和引导装载程序(GRUB)

启动

内核引导,内核init初始化,启动 systemd,其是所有进程之父。

Android系统启动

这里Android启动流程系统稍微有点不一样。
这里借别人的一幅图:
启动流程.png

到了BootLoader内核引导程序的装载之后,会启动Linux内核。此时Linux内核就去启动第一个进程,init进程。

启动进程,那么一定会启动到init.cpp中的main函数。

文件:/system/core/init/init.cpp

Parser& parser = Parser::GetInstance();
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");

此时会通过Parser方法去解析当前目录下的init.rc文件中的语法。
文件开头会配置下面这个文件:

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

这里稍微提一下rc的语法,import的意思和js里面css的import意思很相近,就是解析这个文件里面的内容。

此时会判断当前系统的位数是多少。现在就以32位为例子。
此时对应的文件就会替换中间的变量变成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 audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks

此时rc的脚本,意思是service是启动一个服务名字是zygote,通过/system/bin/app_process。 此时为zygote创建socket,访问权限600,onrestrat是指当这些服务需要重新启动时,执行重新启动。更多相关可以去看看:/system/core/init/readme.txt

我们转到/frameworks/base/cmds/app_process/app_main.cpp

找到下个核心,Zygote启动主函数。

###Zygote(孵化进程)启动
Zygote从名字上就知道,这个初始进程的作用像一个母鸡一样,孵化所有的App的进程。

int main(int argc, char* const argv[])
{
    ...
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ...
    // Parse runtime arguments.  Stop at first unrecognized option.
//解析命令
    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;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        // We're not in zygote mode, the only argument we need to pass
        // to RuntimeInit is the application argument.
        //
        // The Remainder of args get passed to startup class main(). Make
        // copies of them before we overwrite them with the process name.
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);
    } else {
...
//设置启动模式
    if (zygote) {
        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.");
        return 10;
    }
}

实际上这里主要分两步:
第一步:解析刚才传进来的配置。
第二步:根据配置来决定app_main启动模式

根据我们的命令我们可以知道系统启动调用的命令会使得zygote标识位为true。

此时进入到ZygoteInit里面初始化。在这之前,我们看看这个runtime对象在start方法做了什么事情。AppRuntime继承于AndroidRuntime

文件:/frameworks/base/core/jni/AndroidRuntime.cpp

AndroidRuntime::start
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());

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

    /*
     * 'startSystemServer == true' means runtime is obsolete and not run from
     * init.rc anymore, so we print out the boot start event here.
     */
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000;
           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
        }
    }
//确认Android目录环境
    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /android does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
//启动虚拟机
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

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

//准备好环境之后,为传入的选项转化为java 层的String对象
    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    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);
    }

//根据传进来的class name,反射启动对应的类
    /*
     * 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);

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

这个方法实际上包含了极大量的工作。此时我们能看到的是Zygote从app_main的入口想要启动ZyogteInit的类。我们看到这个签名就知道这是Android系统启动以来第一个要加载的Java类。那么此时我们必须保证虚拟机启动完成。

所以AndroidRunTime要进行的步骤一共为三大步骤:

1.初始化虚拟机
2.注册jni方法
3.反射启动ZygoteInit。

1.初始化虚拟机

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

我们来研究看看这几个方法究竟做了啥。
先看看jni_invocation的初始化方法

    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2";
static const char* kDebuggableSystemProperty = "ro.debuggable";
#endif
static const char* kLibraryFallback = "libart.so";

bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__
  char buffer[PROP_VALUE_MAX];
#else
  char* buffer = NULL;
#endif
  library = GetLibrary(library, buffer);
  // Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed.
  // This is due to the fact that it is possible that some threads might have yet to finish
  // exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is
  // unloaded.
  const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
  handle_ = dlopen(library, kDlopenFlags);
  if (handle_ == NULL) {
    if (strcmp(library, kLibraryFallback) == 0) {
      // Nothing else to try.
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
    // Note that this is enough to get something like the zygote
    // running, we can't property_set here to fix this for the future
    // because we are root and not the system user. See
    // RuntimeInit.commonInit for where we fix up the property to
    // avoid future fallbacks. http://b/11463182
    ALOGW("Falling back from %s to %s after dlopen error: %s",
          library, kLibraryFallback, dlerror());
    library = kLibraryFallback;
    handle_ = dlopen(library, kDlopenFlags);
    if (handle_ == NULL) {
      ALOGE("Failed to dlopen %s: %s", library, dlerror());
      return false;
    }
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
                  "JNI_GetDefaultJavaVMInitArgs")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
                  "JNI_GetCreatedJavaVMs")) {
    return false;
  }
  return true;
}

核心方法实际上只有一处:

library = GetLibrary(library, buffer);
handle_ = dlopen(library, kDlopenFlags);

dlopen是指读取某个链接库。这个library是通过getlibrary获得的。这个方法就不贴出来了。这个方法解释就是通过读取kLibrarySystemProperty下的属性,来确定加载什么so库。而这个so库就是我们的虚拟机so库。

如果对那些做过编译Android机子的朋友就能明白,实际上这个属性在/art/Android.mk中设定的。里面设定的字符串一般是libart.so或者libdvm.so.这两个分别代表这是art虚拟机还是dvm虚拟机。

当然这是允许自己编写虚拟机,自己设置的。因此,在这个方法的最后会检测,这个so是否包含以下三个函数:

  1. JNI_GetDefaultJavaVMInitArgs
  2. JNI_CreateJavaVM
  3. JNI_GetCreatedJavaVMs

只有包含这三个函数,Android初始化才会认可这个虚拟机做进一步的初始化工作。

这么实现很眼熟是不是?不错这个实现,实际上就和代理模式+工厂模式如出一辙。

这样在Android framework初始化的时候不必要关注虚拟机是什么,将初始化任务代理交给JniInvocation来完成。同时,Android.mk作为工厂来决定Android系统加载什么虚拟机。

因此,JniInvocation这个类抽离了三个方法出来:

JniInvocation::JniInvocation() :
    handle_(NULL),
    JNI_GetDefaultJavaVMInitArgs_(NULL),
    JNI_CreateJavaVM_(NULL),
    JNI_GetCreatedJavaVMs_(NULL) {

  LOG_ALWAYS_FATAL_IF(jni_invocation_ != NULL, "JniInvocation instance already initialized");
  jni_invocation_ = this;
}

如果不是这是几个so库无法通过接口实现来完成,那么这个设计模式uml如下:

jvm初始化.png

这里我将不涉及虚拟机初始化的流程,我将会专门开一个文章来总结。

此时我们已经初始化好了art虚拟机(此时是Android 7.0),我们要开始启动虚拟机了。

2.启动化虚拟机

    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

在startVm中做了两个工作,第一个把对虚拟机设置的参数设置进去,第二点,调用虚拟机的JNI_CreateJavaVM方法。核心方法如下:

    /*
     * Initialize the VM.
     *
     * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
     * If this call succeeds, the VM is ready, and we can start issuing
     * JNI calls.
     */
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        return -1;
    }

    return 0;

此时如果创建成功,返回的int是大于等于0,小于0则是创建异常。并且把初始化好之后的数据类型赋值给JavaVm,JniEnv。

而下面这个函数onVmCreated(env);

void AndroidRuntime::onVmCreated(JNIEnv* env)
{
    // If AndroidRuntime had anything to do here, we'd have done it in 'start'.
}

是不做任何处理的。这里等下说,还有什么妙用。

2.注册jni方法

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

我们看看这个注册是做什么的。

/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    ATRACE_NAME("RegisterAndroidNatives");
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM.  (This needs to go away in favor of JNI
     * Attach calls.)
     */
    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;
}

实际上这个核心方法是register_jni_procs。注册jni方法。

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {
#ifndef NDEBUG
            ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
            return -1;
        }
    }
    return 0;
}

RegJNIRec这个是一个结构体。mProc就是每个方法的方法指针:

struct RegJNIRec {
        int (*mProc)(JNIEnv*);
    };

每一次都会调用注册这个数组中的方法。我们看看这个array的数组都有什么。

static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_com_android_internal_os_RuntimeInit),
  ...
    REG_JNI(register_com_android_internal_os_Zygote),
    REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
    REG_JNI(register_android_hardware_Camera),
    REG_JNI(register_android_hardware_camera2_CameraMetadata),
    REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
    REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
    REG_JNI(register_android_hardware_camera2_DngCreator),
    REG_JNI(register_android_hardware_Radio),
    REG_JNI(register_android_hardware_SensorManager),
    REG_JNI(register_android_hardware_SerialPort),
    REG_JNI(register_android_hardware_SoundTrigger),
    REG_JNI(register_android_hardware_UsbDevice),
    REG_JNI(register_android_hardware_UsbDeviceConnection),
    REG_JNI(register_android_hardware_UsbRequest),
    ....
};

看到了把。这些方法无一不是我们熟悉的方法,什么Bitmap,Parcel,Camera等等api,这些需要调用native的类。

这就说回来了,onVmCreated这个留下的空函数做什么的。一般我们开发留下一个空函数一般不是兼容版本,就是交给用户做额外处理。实际我们开发jni的时候,我们可以通过这个方法,常做的两件事情,第一,创建一个JniEnv并为其绑定线程,第二,我们可以在这个时刻做好方法映射,让我们内部的方法,不需要遵守jni的命名规则(包+方法名),这样能够大大的扩展我们so的编写的可读性和复用。

3.反射启动ZygoteInit

    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);

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

此时就走到反射调用ZygoteInit流程,此时传进来的slashClassName就是ZygoteInit的包名。

        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
        }

启动结束,就释放掉,java层的String类,以及为JavaVM接触当前线程的绑定,并回收掉JavaVm内存。

ZygoteInit

此时进入到ZygoteInit的类中。我们看看main方法

文件:/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String argv[]) {
        // Mark zygote start. This ensures that thread creation will throw
        // an error.
        ZygoteHooks.startZygoteNoThreadCreation();

        try {
            Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
            RuntimeInit.enableDdms();
            // Start profiling the zygote initialization.
            SamplingProfilerIntegration.start();

            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
//解析传进来的命令,主要是确定是否要启动SystemServer
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = 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]);
                }
            }

            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }
//注册监听
            registerZygoteSocket(socketName);
//准备资源
            preload();
//装载好虚拟机之后,做一次gc
           gcAndFinalize();
          ...

            // Zygote process unmounts root storage spaces.
            Zygote.nativeUnmountStorageOnInit();

            ZygoteHooks.stopZygoteNoThreadCreation();
//启动SystemServer
            if (startSystemServer) {
                startSystemServer(abiList, socketName);
            }
//启动循环
            Log.i(TAG, "Accepting command socket connections");
            runSelectLoop(abiList);

            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }

这里做的事情主要是四步:
第一步:解析命令是否要启动SystemServer
第二步:registerZygoteSocket来打开socket监听命令,是否孵化新的进程
第三步:启动SystemServer
第四步:runSelectLoop 启动一个系统的looper循环

第一步很简单,就跳过。直接看看第二步registerZygoteSocket

    private static void registerZygoteSocket(String socketName) {
        if (sServerSocket == null) {
            int fileDesc;
            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
            try {
                String env = System.getenv(fullSocketName);
                fileDesc = Integer.parseInt(env);
            } catch (RuntimeException ex) {
                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
            }

            try {
                FileDescriptor fd = new FileDescriptor();
                fd.setInt$(fileDesc);
                sServerSocket = new LocalServerSocket(fd);
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex);
            }
        }
    }

通过创建一个文件描述符,对这个文件描述符进行监听,来做到一个本地socket的功能。

很有趣,没想到socket还能这么创建,平时我们使用socket的时候往往都是使用ServerSocket这些类去完成,没想到可以使用监听文件描述符来办到socket的监听,不过实际上想想Linux下一切皆文件,也就不再觉得意料之外。

之后这个sServerSocket对象将会作为Zygote作为监听外侧的耳目。

启动SystemServer

    private static boolean startSystemServer(String abiList, String socketName)
            throws MethodAndArgsCaller, RuntimeException {
...
        ZygoteConnection.Arguments parsedArgs = null;

 String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009,3010",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "com.android.server.SystemServer",
        };
        int pid;

        try {
            parsedArgs = new ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
//启动新的进程forkSystemServer
            /* Request to fork the system server process */
            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) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            handleSystemServerProcess(parsedArgs);
        }

        return true;
    }

这个方法分两步:
1.fork出SystemServer进程
2.初始化SystemServer

1.fork出SystemServer进程

这个步骤的核心方法是fork出SystemServer进程
我们稍微往这个方法下面看看,究竟有什么问题?

public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
        VM_HOOKS.preFork();
        int pid = nativeForkSystemServer(
                uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
        // Enable tracing as soon as we enter the system_server.
       if (pid == 0) {
            Trace.setTracingEnabled(true);
        }
        VM_HOOKS.postForkCommon();
}

核心方法是调用native方法进行fork。
文件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);
  if (pid > 0) {
      // The zygote process checks whether the child process has died or not.
      ALOGI("System server process %d has been created", pid);
      gSystemServerPid = pid;
      // There is a slight window that the system server process has crashed
      // but it went unnoticed because we haven't published its pid yet. So
      // we recheck here just to make sure that all is well.
      int status;
      if (waitpid(pid, &status, WNOHANG) == pid) {
          ALOGE("System server process %d has died. Restarting Zygote!", pid);
          RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
      }
  }
  return pid;
}

ForkAndSpecializeCommon这个方法就是所有进程fork都会调用这个这个native函数。这个函数实际上是调用linux的fork函数来拷贝出新的进程。

2.初始化SystemServer进程
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            handleSystemServerProcess(parsedArgs);
        }

孵化好新的进程。这里返回结果。加入结果pid是等于0.说明会走这个分支。我们这里只看一个孵化进程的情况。看看这个方法handleSystemServerProcess(parsedArgs);

    private static void handleSystemServerProcess(
            ZygoteConnection.Arguments parsedArgs)
            throws ZygoteInit.MethodAndArgsCaller {

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

        /* should never reach here */
    }

由于这个新的进程不需要接收消息,去孵化进程,所以第一件事情就是关闭socket的监听。第二件事就是通过RuntimeInit初始化SystemServer。

文件:com/android/internal/os/RuntimeInit.java

    public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");
        redirectLogStreams();

        commonInit();
        nativeZygoteInit();
        applicationInit(targetSdkVersion, argv, classLoader);
    }

此时nativeZygoteInit会初始化我们ProcessState时候,同时初始化我们鼎鼎大名的Binder驱动。

我们看看核心方法applicationInit做了啥:

   private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
...
        // Remaining arguments are passed to the start class's static main
        invokeStaticMain(args.startClass, args.startArgs, classLoader);
    }

就是这个方法把传进来的参数初始化,把之前“com.android.server.SystemServer”参数反射其main方法。

   public static void main(String[] args) {
        new SystemServer().run();
    }

这里初始化“android_servers”的so库,里面包含了大量的Android的native需要加载的native服务,以及我们经常会用到各种服务(Display,powermanager,alarm,inputmanager,AMS,WMS)。创建了我们熟悉的SystemServerManger,为这个进程创建了Looper。

runSelectLoop启动一个Zygote的循环。

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
//sServerSocket中文件描述符的集合
        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 (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
            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);
                    }
                }
            }
        }
    }

先介绍下面两个队列

        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);
1.fds 文件描述符的队列.一般情况下,这个队列从头到尾只有一个,保证了当前只有一个Zygote在等待外面的监听。
2.peers ZygoteConnection队列。这个队列决定还有多少个socket事件没有处理。

这是构建的一段死循环。可以说是系统主Loop,就像iOS,ActivityThread中的Looper,用来响应外界的事件。

 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 (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }
            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中监听到了数据过来,将会执行runOnce()方法,接着把对应的socket等待处理事件以及,对应的socket文件符移除掉。此时事件队列为0,这个循环再一次把Zygote监听添加回来。

runOnce

我们看看runOnce方法

    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

        String args[];
        Arguments parsedArgs = null;
        FileDescriptor[] descriptors;

        try {
            args = readArgumentList();
            descriptors = mSocket.getAncillaryFileDescriptors();
        } catch (IOException ex) {
            Log.w(TAG, "IOException on command socket " + ex.getMessage());
            closeSocket();
            return true;
        }

        if (args == null) {
            // EOF reached.
            closeSocket();
            return true;
        }

        /** the stderr of the most recent request, if avail */
        PrintStream newStderr = null;

        if (descriptors != null && descriptors.length >= 3) {
            newStderr = new PrintStream(
                    new FileOutputStream(descriptors[2]));
        }

        int pid = -1;
        FileDescriptor childPipeFd = null;
        FileDescriptor serverPipeFd = null;

        try {
//读取socket数据
            parsedArgs = new Arguments(args);

            if (parsedArgs.abiListQuery) {
                return handleAbiListQuery();
            }

            ...
            fd = null;
//fork子进程
            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                    parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                    parsedArgs.appDataDir);
        } catch (ErrnoException ex) {
            logAndPrintError(newStderr, "Exception creating pipe", ex);
        } catch (IllegalArgumentException ex) {
            logAndPrintError(newStderr, "Invalid zygote arguments", ex);
        } catch (ZygoteSecurityException ex) {
            logAndPrintError(newStderr,
                    "Zygote security policy prevents request: ", ex);
        }

        try {
            if (pid == 0) {
                // in child
                IoUtils.closeQuietly(serverPipeFd);
                serverPipeFd = null;
                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

                // should never get here, the child is expected to either
                // throw ZygoteInit.MethodAndArgsCaller or exec().
                return true;
            } else {
                // in parent...pid of < 0 means failure
                IoUtils.closeQuietly(childPipeFd);
                childPipeFd = null;
//处理子进程的后续
                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
            }
        } finally {
            IoUtils.closeQuietly(childPipeFd);
            IoUtils.closeQuietly(serverPipeFd);
        }
    }

runOnce做的事情分为三部分:

    1. 调用readArgumentList读取socket传过来的数据
    1. 通过 forkAndSpecialize fork新进程
    1. 处理fork出来的子进程
handleParentProc
    private boolean handleParentProc(int pid,
            FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {

        if (pid > 0) {
            setChildPgid(pid);
        }

        if (descriptors != null) {
            for (FileDescriptor fd: descriptors) {
                IoUtils.closeQuietly(fd);
            }
        }

        boolean usingWrapper = false;
        if (pipeFd != null && pid > 0) {
            DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
            int innerPid = -1;
            try {
                innerPid = is.readInt();
            } catch (IOException ex) {
                Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
            } finally {
                try {
                    is.close();
                } catch (IOException ex) {
                }
            }
...

        try {
            mSocketOutStream.writeInt(pid);
            mSocketOutStream.writeBoolean(usingWrapper);
        } catch (IOException ex) {
            Log.e(TAG, "Error writing to command socket", ex);
            return true;
        }

        return false;
    }

子进程关闭fd入口,同时设置好pid,并且把pid带给fork出来的新进程。

这里按照惯例,我们画一幅时序图,总结

Android系统启动.png

###Zygote 进程间通信原理
不熟悉Linux编程的同学看到死循环最后这一段,可能就有点懵。这里我解释一遍,在构造一下整个流程以及模型估计就能明白了。

虽然是socket通信,但是实际上和我们常说Java的socket编程稍微有一点点不一样。实际上更加像驱动中的文件描述的监听。这里和Android4.4的有点不一样,但是思路是一样。

Zygote监听服务端

从上面的代码,根据我的理论,peers这个ZygoteConnection是一个Zygote的链接对象,用来处理从远端的socket过来的消息。这个是一个关键类。我们看看这个ZygoteConnection究竟是怎么构造的。

    private static ZygoteConnection acceptCommandPeer(String abiList) {
        try {
            return new ZygoteConnection(sServerSocket.accept(), abiList);
        } catch (IOException ex) {
            throw new RuntimeException(
                    "IOException during accept()", ex);
        }
    }

实际上此处会new一个ZygoteConnection,会把LocalServerSocket的accpet传进去。此时就和普通的socket一样进入阻塞。

让我先把LocalSocket这一系列的UML图放出来就能明白,这几个类之间关系。
LocalSocket uml.png
实际上,所有的LocalSocket,无论是服务端LocalServerSocket还是客户端LocalSocket都是通过LocalServerImpl实现的。

protected void accept(LocalSocketImpl s) throws IOException {
        if (fd == null) {
            throw new IOException("socket not created");
        }

        try {
            s.fd = Os.accept(fd, null /* address */);
            s.mFdCreatedInternally = true;
        } catch (ErrnoException e) {
            throw e.rethrowAsIOException();
        }
    }

这个Os对象通过Libcore.os.accept(fd, peerAddress);调用native层。
文件:/libcore/luni/src/main/native/libcore_io_Posix.cpp

static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
    sockaddr_storage ss;
    socklen_t sl = sizeof(ss);
    memset(&ss, 0, sizeof(ss));
//判断java层的socket对象是否为NULL
    sockaddr* peer = (javaSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
    socklen_t* peerLength = (javaSocketAddress != NULL) ? &sl : 0;

//核心,此处等待阻塞线程
    jint clientFd = NET_FAILURE_RETRY(env, int, accept, javaFd, peer, peerLength);
    if (clientFd == -1 || !fillSocketAddress(env, javaSocketAddress, ss, *peerLength)) {
        close(clientFd);
        return NULL;
    }
//一旦socket回调之后,将会通过底层的fd对象转化为java对象
    return (clientFd != -1) ? jniCreateFileDescriptor(env, clientFd) : NULL;
}

此处分为三步:

  • 第一步,通过解析address是否为空,来决定阻塞的等待时长,此时传下来为null,为无限期的等待。
  • 第二步,核心方法,通过define声明的NET_FAILURE_RETRY代码段,阻塞线程
  • 第三步,一旦等待的socket链接有数据回调进来,则转化为java层的fd返回。

此处是阻塞的核心代码

#define NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
    return_type _rc = -1; \
    int _syscallErrno; \
    do { \
        bool _wasSignaled; \
        { \
//转化java的fd,对Java进行监听
            int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
            AsynchronousCloseMonitor _monitor(_fd); \
            _rc = syscall_name(_fd, __VA_ARGS__); \
            _syscallErrno = errno; \
            _wasSignaled = _monitor.wasSignaled(); \
        } \
        if (_wasSignaled) { \
            jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
            _rc = -1; \
            break; \
        } \
        if (_rc == -1 && _syscallErrno != EINTR) { \
            /* TODO: with a format string we could show the arguments too, like strace(1). */ \
            throwErrnoException(jni_env, # syscall_name); \
            break; \
        } \
    } while (_rc == -1); /* _syscallErrno == EINTR && !_wasSignaled */ \
    if (_rc == -1) { \
        /* If the syscall failed, re-set errno: throwing an exception might have modified it. */ \
        errno = _syscallErrno; \
    } \
    _rc; })

这里稍微解释一下,这段阻塞的核心方法的意思。
这循环代码的跳出条件有三个:

  • _wasSignaled 为true 也就是说此时AsynchronousCloseMonitor通过线程锁ScopeThreadMutex上锁的线程被唤醒,说明了该socket断开,也就断开了阻塞。

  • _rc 为-1 以及 _syscallErrno 错误标示位不为EINTER。rc为syscall_name(此时传进来的是socket的accept方法)。也就是说当accept链接出现异常的时候(返回-1)会一直在循环里面等待,除非为全局错误_syscallErrno 不是系统抛出的中断,则抛出异常。

  • 当_rc不为-1,也就是说socket链接成功。则继续向下走。

因此从这里可以知道,Zygote在初始化runSelectLoop的时候,一开始会加入一个ZygoteConnection用于阻塞监听。一旦有链接进来,则唤醒则加入到peers队列中。在死循环下一个轮回的时候,通过执行runOnce执行fork新的进程。

虽然到这里似乎就完成整个流程了。但是实际上,google工程师写代码才不会这么简单就完成,而是做了一定的优化。

如果用Linux c写过服务器的哥们,就会明白这样不断的阻塞只会不断的消耗的cpu的资源,并不是很好的选择。

因此,runSelectLoop才有这一段代码

            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 (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }

根据这段代码,从表面上可以清楚的知道,一开始把描述符都设置进去StructPollfd等长数组中。把这个数组交给Os.poll中。

我们先看看StructPollfd这个类是个什么存在。
文件/libcore/luni/src/main/java/android/system/StructPollfd.java

public final class StructPollfd {
  /** The file descriptor to poll. */
  public FileDescriptor fd;

  /**
   * The events we're interested in. POLLIN corresponds to being in select(2)'s read fd set,
   * POLLOUT to the write fd set.
   */
  public short events;

  /** The events that actually happened. */
  public short revents;

  /**
   * A non-standard extension that lets callers conveniently map back to the object
   * their fd belongs to. This is used by Selector, for example, to associate each
   * FileDescriptor with the corresponding SelectionKey.
   */
  public Object userData;

  @Override public String toString() {
    return Objects.toString(this);
  }
}

这个类十分简单。里面只有那么3个参数,events,revents,fd.分别是做什么的呢?

我们直接看看Os.poll方法底层的实现
文件:/libcore/luni/src/main/native/libcore_io_Posix.cpp

static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) {

//反射获取structPollfd.java属性的属性id
    static jfieldID fdFid = env->GetFieldID(JniConstants::structPollfdClass, "fd", "Ljava/io/FileDescriptor;");
    static jfieldID eventsFid = env->GetFieldID(JniConstants::structPollfdClass, "events", "S");
    static jfieldID reventsFid = env->GetFieldID(JniConstants::structPollfdClass, "revents", "S");
//转化为ndk底层的文件描述符
    // Turn the Java android.system.StructPollfd[] into a C++ struct pollfd[].
    size_t arrayLength = env->GetArrayLength(javaStructs);
    std::unique_ptr<struct pollfd[]> fds(new struct pollfd[arrayLength]);
    memset(fds.get(), 0, sizeof(struct pollfd) * arrayLength);
    size_t count = 0; // Some trailing array elements may be irrelevant. (See below.)
    for (size_t i = 0; i < arrayLength; ++i) {
        ScopedLocalRef<jobject> javaStruct(env, env->GetObjectArrayElement(javaStructs, i));
        if (javaStruct.get() == NULL) {
            break; // We allow trailing nulls in the array for caller convenience.
        }
        ScopedLocalRef<jobject> javaFd(env, env->GetObjectField(javaStruct.get(), fdFid));
        if (javaFd.get() == NULL) {
            break; // We also allow callers to just clear the fd field (this is what Selector does).
        }
        fds[count].fd = jniGetFDFromFileDescriptor(env, javaFd.get());
        fds[count].events = env->GetShortField(javaStruct.get(), eventsFid);
        ++count;
    }

    std::vector<AsynchronousCloseMonitor*> monitors;
    for (size_t i = 0; i < count; ++i) {
        monitors.push_back(new AsynchronousCloseMonitor(fds[i].fd));
    }
//循环监听
    int rc;
    while (true) {
        timespec before;
        clock_gettime(CLOCK_MONOTONIC, &before);
//poll 阻塞进程
        rc = poll(fds.get(), count, timeoutMs);
        if (rc >= 0 || errno != EINTR) {
            break;
        }

        // We got EINTR. Work out how much of the original timeout is still left.
        if (timeoutMs > 0) {
            timespec now;
            clock_gettime(CLOCK_MONOTONIC, &now);

            timespec diff;
            diff.tv_sec = now.tv_sec - before.tv_sec;
            diff.tv_nsec = now.tv_nsec - before.tv_nsec;
            if (diff.tv_nsec < 0) {
                --diff.tv_sec;
                diff.tv_nsec += 1000000000;
            }

            jint diffMs = diff.tv_sec * 1000 + diff.tv_nsec / 1000000;
            if (diffMs >= timeoutMs) {
                rc = 0; // We have less than 1ms left anyway, so just time out.
                break;
            }

            timeoutMs -= diffMs;
        }
    }

    for (size_t i = 0; i < monitors.size(); ++i) {
        delete monitors[i];
    }
    if (rc == -1) {
        throwErrnoException(env, "poll");
        return -1;
    }
//唤醒之后更新runSelectLooper中的revents标识位,revents
    // Update the revents fields in the Java android.system.StructPollfd[].
    for (size_t i = 0; i < count; ++i) {
        ScopedLocalRef<jobject> javaStruct(env, env->GetObjectArrayElement(javaStructs, i));
        if (javaStruct.get() == NULL) {
            return -1;
        }
        env->SetShortField(javaStruct.get(), reventsFid, fds[i].revents);
    }
    return rc;
}

这个代码做了三件事情:

  • 1.通过反射获取structPollfd.java中fd属性,revents,events属性。把这些参数设置到pollfd[] fds队列中。
  • 2.把fds设置到poll进行监听
  • 3.更新java层的structPollfd队列。

核心是第二步骤,linux的poll的函数。
而poll函数的作用就是如果没有检测到文件描述符的变化,则进程进入到睡眠状态,等到有人唤醒。由于此时传入的timeout为0,则不设置超时等待时间。

那么我们可以清楚的知道了,structPollfd做三个属性是什么。

  • 第一个文件描述符,用来poll监听该文件描述符是否出现了变化。在这里还记得,传入的是zygote的socket文件吗?也就是说此时poll在监听socket是否出现了变化。

  • 第二个event,作为pollfd中事件掩码的参数

  • 第三个revent,代表了该文件描述符是否产生了变化。

因此,在每一次调用完Os.poll之后,如果socket有唤醒之后,会更新StructPollfd中的数据,也就有了下面这段判断逻辑

            for (int i = pollFds.length - 1; i >= 0; --i) {
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }
...
             }

唤醒之后直接循环pollFds中,判断revents是否有变化,和POLLIN(实际上是0)相于不为0则表示socket文件变化了,才有下面的加入peers列表以及通过runOnce启动进程。

通过这样的优化,就能做到,当没有socket接入的时候,进程休眠,腾出了cpu资源。当socket接入,则唤醒进程,进入到accept,等待数据的接入。这样就能大大的提升了其中的资源利用率。(一些普通的web服务器也是如此的设计的)

这里只是解释了LocalSocket的服务端。

Zygote 客户端

实际上一般的ZygoteSocket的客户端,一般为SystemServer中的ActivitymanagerService.

我们看看在Android 7.0中当不存在对应的应用进程时候,会调用startProcessLocked方法中Process的start方法。
最终会调用

 public static final String ZYGOTE_SOCKET = "zygote";


    private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
        if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
            try {
                primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
            }
        }

        if (primaryZygoteState.matches(abi)) {
            return primaryZygoteState;
        }

        // The primary zygote didn't match. Try the secondary.
        if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
            try {
            secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET);
            } catch (IOException ioe) {
                throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
            }
        }

        if (secondaryZygoteState.matches(abi)) {
            return secondaryZygoteState;
        }

        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
    }

这里的核心会调用一次ZygoteState的connect方法。

        public static ZygoteState connect(String socketAddress) throws IOException {
            DataInputStream zygoteInputStream = null;
            BufferedWriter zygoteWriter = null;
            final LocalSocket zygoteSocket = new LocalSocket();

            try {
                zygoteSocket.connect(new LocalSocketAddress(socketAddress,
                        LocalSocketAddress.Namespace.RESERVED));

                zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());

                zygoteWriter = new BufferedWriter(new OutputStreamWriter(
                        zygoteSocket.getOutputStream()), 256);
            } catch (IOException ex) {
                try {
                    zygoteSocket.close();
                } catch (IOException ignore) {
                }

                throw ex;
            }

            String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
            Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);

            return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                    Arrays.asList(abiListString.split(",")));
        }

此时会尝试的通过zygoteSocket也就是LocalSocket 去连接名为zygote的socket。也就是我们最开始初始化的在ZygoteInit中registerZygoteSocket的socket名字。

调用connect方法,唤醒Os.poll方法之后,再唤醒LocalServerSocket.accept方法,在循环的下一个,调用runOnce。

那么zygote又是怎么启动ActivityThread,这个应用第一个启动的类呢?

第一次看runOnce代码的老哥可能会被这一行蒙蔽了:

ZygoteConnection newPeer = acceptCommandPeer(abiList);

实际上在ZygoteConnection中,这个abiList不起任何作用。真正起作用的是ZygoteConnection.runOnce中readArgumentList
方法。
文件/frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

private String[] readArgumentList()
            throws IOException {

        /**
         * See android.os.Process.zygoteSendArgsAndGetPid()
         * Presently the wire format to the zygote process is:
         * a) a count of arguments (argc, in essence)
         * b) a number of newline-separated argument strings equal to count
         *
         * After the zygote process reads these it will write the pid of
         * the child or -1 on failure.
         */

        int argc;

        try {
            String s = mSocketReader.readLine();

            if (s == null) {
                // EOF reached.
                return null;
            }
            argc = Integer.parseInt(s);
        } catch (NumberFormatException ex) {
            Log.e(TAG, "invalid Zygote wire format: non-int at argc");
            throw new IOException("invalid wire format");
        }

        // See bug 1092107: large argc can be used for a DOS attack
        if (argc > MAX_ZYGOTE_ARGC) {
            throw new IOException("max arg count exceeded");
        }

        String[] result = new String[argc];
        for (int i = 0; i < argc; i++) {
            result[i] = mSocketReader.readLine();
            if (result[i] == null) {
                // We got an unexpected EOF.
                throw new IOException("truncated request");
            }
        }

        return result;
    }

看吧实际上所有的字符串都是通过zygote的SocketReader读取出来,再赋值给上层。进行fork出新的进程。
在ActivityManagerSerevice的startProcessLocked

if (entryPoint == null) entryPoint = "android.app.ActivityThread";

    Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);

第一个参数就是ActivityThread,通过start方法,来打runOnce之后,进去handleChildProc,把ActivityThread的main反射出来,开始了Activity的初始化。

至此,从Linux内核启动到应用的AcivityThread的大体流程就完成了。

优化与思考

Android系统这么写Zygote孵化流程真的最佳的吗?辉哥曾经提问过一个问题,framework的启动流程该怎么优化。

我们去翻翻4.4的整个流程和android 7.0做对比。发现除了加载虚拟机是从art变成dvm之外,其他逻辑大体上一致。

唯一不同的就是runSelectLoop方法出现了变化。
android 4.4.4
/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

static final int GC_LOOP_COUNT = 10;

private static void runSelectLoop() throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
        FileDescriptor[] fdArray = new FileDescriptor[4];

        fds.add(sServerSocket.getFileDescriptor());
        peers.add(null);

        int loopCount = GC_LOOP_COUNT;
        while (true) {
            int index;

            /*
             * Call gc() before we block in select().
             * It's work that has to be done anyway, and it's better
             * to avoid making every child do it.  It will also
             * madvise() any free memory as a side-effect.
             *
             * Don't call it every time, because walking the entire
             * heap is a lot of overhead to free a few hundred bytes.
             */
//做一次gc为了给每个子进程腾出内存空间
            if (loopCount <= 0) {
                gc();
                loopCount = GC_LOOP_COUNT;
            } else {
                loopCount--;
            }

//每一次通过select检测array中的fd有什么变化。
            try {
                fdArray = fds.toArray(fdArray);
                index = selectReadable(fdArray);
            } catch (IOException ex) {
                throw new RuntimeException("Error in select()", ex);
            }

//下面的逻辑一样和之前的一样
            if (index < 0) {
                throw new RuntimeException("Error in select()");
            } else if (index == 0) {
                ZygoteConnection newPeer = acceptCommandPeer();
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                boolean done;
                done = peers.get(index).runOnce();

                if (done) {
                    peers.remove(index);
                    fds.remove(index);
                }
            }
        }
    }

这里稍微解释一下,在低版本fds和peers的意义还是没有多少变动,多了一个限制一次性最多也就4个ZygoteConnection监听。主要去看看下面的死循环之前的操作。

            try {
                fdArray = fds.toArray(fdArray);
                index = selectReadable(fdArray);
            } catch (IOException ex) {
                throw new RuntimeException("Error in select()", ex);
            }

这里面的代码实际上和上面的Os.poll那一段的类似。是为了监听socket中哪些出现了变化,而后唤醒进程。
这个方法直接调用的是native方法。

static jint com_android_internal_os_ZygoteInit_selectReadable (
        JNIEnv *env, jobject clazz, jobjectArray fds)
{
...
    FD_ZERO(&fdset);
//获取ndk层的fd
    int nfds = 0;
    for (jsize i = 0; i < length; i++) {
        jobject fdObj = env->GetObjectArrayElement(fds, i);
        if  (env->ExceptionOccurred() != NULL) {
            return -1;
        }
        if (fdObj == NULL) {
            continue;
        }
        int fd = jniGetFDFromFileDescriptor(env, fdObj);
        if  (env->ExceptionOccurred() != NULL) {
            return -1;
        }

        FD_SET(fd, &fdset);

        if (fd >= nfds) {
            nfds = fd + 1;
        }
    }
//select死循环阻塞
    int err;
    do {
        err = select (nfds, &fdset, NULL, NULL, NULL);
    } while (err < 0 && errno == EINTR);

    if (err < 0) {
        jniThrowIOException(env, errno);
        return -1;
    }
//查看哪些fd出现了变化,把index回调上去
    for (jsize i = 0; i < length; i++) {
        jobject fdObj = env->GetObjectArrayElement(fds, i);
        if  (env->ExceptionOccurred() != NULL) {
            return -1;
        }
        if (fdObj == NULL) {
            continue;
        }
        int fd = jniGetFDFromFileDescriptor(env, fdObj);
        if  (env->ExceptionOccurred() != NULL) {
            return -1;
        }
        if (FD_ISSET(fd, &fdset)) {
            return (jint)i;
        }
    }
    return -1;
}

这个函数分为三个部分:

  • 1.从java层获取fd的对象,通过jniGetFDFromFileDescriptor转化为具体的fd。每一次都加一个一,为select函数做准备。
  • 2.调用select,监听所有的文件描述符中的变化
  • 3.寻找变化的文件描述符(socket)对应的index,唤醒并且接受socket。

如果不太懂Linux api select函数,这里放出一个写select的比较好的博文:
https://www.cnblogs.com/skyfsm/p/7079458.html

这里简单的解释一下,select的参数。第一个参数,代表了有多少文件描述符加入了,此时只有一个,第二个参数,把fd每个参数对应的标志位,一旦这个标志位出现了变动,则代表这个文件描述符出现变化,socket接入了。其他先可以不管。

因此在最下面的那一段函数中,通过FD_ISSET的方法,判断变动的标志位,找到对应的fd,把对应的index返回。

这样就能正确找到哪个socket。并且处理对应的ZygoteConnection。

上个图总结:
zygote通信原理.png

思考

经过两者的比较,为什么在4.4.4版本使用select()去做,而到了7.0版本使用了poll。为什么这么做?先说说两个函数之间的区别。

简单的说,select和poll本质上都是对文件描述符的集合进行轮询查找,哪些socket出现了变化并且告诉Zygote。然而api的不同导致两者之间的策略不一样。

在4.4时代,大部分的手机内存吃紧(这一点从runLoop每隔10次就要gc一次就知道了),而select的好处就是每一次轮询都是直接修正每一个fd对应的标志位,速度较快。缺点是,一段标志位使用过每一个位上的0或者1来判断,也就限制了最大连接数量。

而7.0时代,大部分手机的性能变得比较好了。资源不再吃紧了,此时更换为poll函数。该函数的作用和select很相似。不过每一次轮询fd,都要修改pollfd结构体内部的标志位。这样就脱离标志位的限制了。

所以说,对于不同的api的,没有最好,只有最适用。

愚见

难道没办法,更好的办法吗?有!这只是个人看法,还记得前几年流行的ngnx吗?这个的底层是用epoll来实现的。

这种实现和单一的阻塞不一样。而是异步的IO。这方法只有Linux 2.6才开始支持。这个方法相比于select和poll。不是简单的轮询,因为当量级到了一定的时候,轮询的速度必定慢下来。而是通过回调的机制去处理。每一次通过内存映射的方式查找对应的fd,并且回调。这样就省去了内存在调用fd时候造成的拷贝(从内核空间到用户空间)。

其次,epoll这个函数没有数量的限制,而是由一个文件描述符去控制所有的文件描述符。

基于这两个理由,很明显epoll才是最佳的选择。

但是,最佳就必须选择吗?不,我们只选择了最合适的。我刚才看了下android 9.0的源码。发现还是继续使用poll机制。对于android来说zygote诞生出新的进程的情况不多见,量级远没有达到服务器的地步,加上使用epoll,下面的fork的机制可能变动大,没有选择也是情理之中。

当然,如果有哥们看过Handler的源码,就知道Handler有一层ndk层,下层也是用epoll做等待死循环处理。有机会再源码解析解析。

总结

实际上最后这一段Zygote孵化原理,我发现老罗的书,还有网上的资料都说不详细,但是这却是最重要的一环,是Zygote沟通应用程序的核心代码。特此在此记录一下。

那么Zygote诞生做了什么?在Activity启动前的角色是什么?现在就明白了。

  • 1.Zygote是init进程之后第一个诞生出来的孵化进程。就以Android系统的framework来说,Zygote是Android系统一切进程的母亲。

  • 2.Zygote第一个孵化的进程是SystemServer进程。

  • 3.初始化虚拟机是通过jniInvoaction,加载对应的so库

  • 4.SystemServer进程初始化,AMS,WMS,PMS,DisplayManager(显示),InputManager(键盘),PowerManager(电源)…

  • 5.Zygote 诞生新的进程都是通过fork诞生的。

  • 6.Zygote 开启socket监听死循环,在低版本使用select来阻塞,高版本使用poll来阻塞。

参考资料:
https://segmentfault.com/a/1190000003063859?utm_source=tag-newest

https://www.cnblogs.com/amanlikethis/p/6915485.html

题外话

写的比较粗浅,也不是很专业。看到错误可以找我纠正。估计很多人都懂这些了,更多的只是把这两年学习的复习和整理。

可以来这个链接下来找我:https://blog.csdn.net/yujunyu12/article/details/87918948
简书居然不允许发表字数多一点的文章,看情况,可能舍弃掉简书了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值