Android Framework- Zygote进程以及其初始化

Zygote 概述

  • Zygote 时android中非常重要的一个进程,它和 init 进程、SystemServer 进程同为支持Android最重要的进程。、
  • Linux 的进程时通过系统调用 fork 产生的,fork 出的子进程除了内核中的一些核心数据结构和父进程不同外,其余的内存影像都是和父进程共享的。只有当子进程需要去修改这些共享内存时,操作系统才会为子进程分配一个新的页面,并将老的页面上的数据复制一份到新的页面,这就是所谓的写时拷贝(Copy On Write)
  • 通常子进程被fork出来后,会继续执行系统调用 exec 。exec将用一个新的可执行文件的内容替换当前的代码块、数据段、堆和栈。fork + exec 是 Linux 启动应用标准做法,init 也是通过这样启动各种服务。
  • Zygote 启动各种应用时却只调用了 fork 没有调用 exec。Android引用中执行的是 java 代码,Java代码不同才造成了应用的不同,而对于Java 运行环境是相同的。
  • Zygote 初始化时会创建虚拟机,同时把需要的系统类库和资源文件加载到内存里。Zygote fork出进程后,这个子进程也继承了能正常工作的虚拟机和各种系统资源,接下来的子进程中需要加在 APK 中的字节码就可以运行了。这样运行时间会大大缩短。
  • 下图时 init 和 Zygote 区别
    在这里插入图片描述

Zygote 进程的初始化

  • Zygote 进程在 init 进程中以 Service 的方式启动的,5.0 以前是直接放在 init.rc 的代码块中,现在是放在了单独的文件中,然后在 init.rc 通过 import 方式引入文件,如下:
import /init.${ro.zygote}.rc

从import中可以看出,init.rc 并不是直接引入了某个固定的文件而是根据ro.zygote的属性来引入不同的文件,这是因为从 5.0 开始android开始支持64位编译,Zygote 本身也会有 32位和64位的区别,因此通过 ro.zygote 来控制启动不同的版本的 Zygote 进程。属性 ro.zygote 可能有“zygote32”、“zygote32_64”、“zygote64”、“zygote64_32”。所以在 init.rc同级目录下可能有四个zygote.rc文件,我在下载 9.0源码就可以搜索出如下结果:
在这里插入图片描述

  • 下面是 zygote32.rc 文件内容
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    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
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

  • 下面是 zygote32_64.rc 文件内容
service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    class main
    priority -20
    user root
    group root readproc reserved_disk
    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
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote_secondary stream 660 root system
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks

从上面两个文件中可以看出 zygote32_64.rc 有两个 Zygote 服务,zygote 和 zygote_secondary,这两个服务最大的区别是启动可执行文件不同,一个是app_process32,另外一个是 app_process64。zygote64 和 zygote64_32 也差不带多,只不过把可执行文件换过来了。从这里可以知道Android支持四种运行模式。

  • 纯32位模式:属性为ro.zygote的值为zygote32
  • 以32位为主,64位为辅:属性为ro.zygote的值为zygote32_64
  • 纯64位模式:属性为ro.zygote的值为zygote64
  • 以64位为主,32位为辅:属性为ro.zygote的值为zygote64_32
  • 从上面 Zygote 文件可以看出 Zygote进程可执行文件是 app_process,app_process文件的源文件位于下图:
    在这里插入图片描述
app_process 的 main 函数
  • main 函数主要功能是解析启动参数。虽然Zygote 进程是通过 app_process 可执行文件创建的,但是app_process 除了可以创建 Zygote 外,还可以创建普通进程。前面介绍 Zygote 进程创建的时候是没有调用 exec ,从执行角度来看是有优点的,但是在某些情况下,例如调试内存分配时,会带来很多麻烦。所以Android也提供了传统方式启动应用。

  • 这两种启动方式是通过参数来区别的

启动格式为:app_process[虚拟机参数] 运行目录 参数 [java 类]

  • 虚拟机参数:以 _ 开头启动虚拟机时传递给虚拟机
  • 运行目录:程序运行目录,通常是 /system/bin
  • 参数:以–开头。参数 --zygote 表示启动 zygote,参数 --application表示以普通进程方式执行Java代码。
  • Java类:将要执行的Java类,必须有一个静态的 main 。使用 -Zygote 时不会执行这个类,而是固定的执行 ZygoteInit 类。
main函数启动流程
  • 创建AppRuntime 对象并保存参数
    AppRuntime是在app_process 中定义的类,继承了系统的 AndroidRuntime类。AndroidRuntime类主要作用是创建初始化虚拟机。
int main(int argc, char* const argv[])
{
    if (!LOG_NDEBUG) {
      String8 argv_String;
      for (int i = 0; i < argc; ++i) {
        argv_String.append("\"");
        argv_String.append(argv[i]);
        argv_String.append("\" ");
      }
      ALOGV("app_process main with argv: %s", argv_String.string());
    }

    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;
    const char* spaced_commands[] = { "-cp", "-classpath" };
    // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
    bool known_command = false;

    int i;
    for (i = 0; i < argc; i++) {
        if (known_command == true) {
          runtime.addOption(strdup(argv[i]));
          // The static analyzer gets upset that we don't ever free the above
          // string. Since the allocation is from main, leaking it doesn't seem
          // problematic. NOLINTNEXTLINE
          ALOGV("app_process main add known option '%s'", argv[i]);
          known_command = false;
          continue;
        }

        for (int j = 0;
             j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
             ++j) {
          if (strcmp(argv[i], spaced_commands[j]) == 0) {
            known_command = true;
            ALOGV("app_process main found known command '%s'", argv[i]);
          }
        }

        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }

        runtime.addOption(strdup(argv[i]));
        ALOGV("app_process main add option '%s'", argv[i]);
    }
  • 解析启动参数
int i;
    for (i = 0; i < argc; i++) {
        if (known_command == true) {
          runtime.addOption(strdup(argv[i]));
          // The static analyzer gets upset that we don't ever free the above
          // string. Since the allocation is from main, leaking it doesn't seem
          // problematic. NOLINTNEXTLINE
          ALOGV("app_process main add known option '%s'", argv[i]);
          known_command = false;
          continue;
        }

        for (int j = 0;
             j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
             ++j) {
          if (strcmp(argv[i], spaced_commands[j]) == 0) {
            known_command = true;
            ALOGV("app_process main found known command '%s'", argv[i]);
          }
        }

        if (argv[i][0] != '-') {
            break;
        }
        if (argv[i][1] == '-' && argv[i][2] == 0) {
            ++i; // Skip --.
            break;
        }

        runtime.addOption(strdup(argv[i]));
        // The static analyzer gets upset that we don't ever free the above
        // string. Since the allocation is from main, leaking it doesn't seem
        // problematic. NOLINTNEXTLINE
        ALOGV("app_process main add option '%s'", argv[i]);
    }

通常是从 init.rc 中传入的参数,“-Xzygote/system/bin --zygote --start-system-server”,解析后得到的结果将是

  • 变量 parentDir 等于 /system/bin
  • 变量 niceName 等于 zygote
  • 变量 startSystemServer 等于 true
  • 变量 zygote 等于 true
  • 准备执行 ZygoteInit 或者 RuntimeInit 类的参数
   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);

        if (!LOG_NDEBUG) {
          String8 restOfArgs;
          char* const* argv_new = argv + i;
          int argc_new = argc - i;
          for (int k = 0; k < argc_new; ++k) {
            restOfArgs.append("\"");
            restOfArgs.append(argv_new[k]);
            restOfArgs.append("\" ");
          }
          ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
        }
    } else {
        // We're in zygote mode.
        maybeCreateDalvikCache();

        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }

        char prop[PROP_VALUE_MAX];
        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
            LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
                ABI_LIST_PROPERTY);
            return 11;
        }

        String8 abiFlag("--abi-list=");
        abiFlag.append(prop);
        args.add(abiFlag);

        // In zygote mode, pass all remaining arguments to the zygote
        // main() method.
        for (; i < argc; ++i) {
            args.add(String8(argv[i]));
        }
    }
  • 将本进程的名称改为–nice-name 指定的字符串。缺省情况下niceName为zygote或者zygote64
    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }
  • 启动 Java 类,如果参数带有 --zygote 执行 zygoteInit类,否则执行传进来的Java类。
    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.");
    }

app_process 除了能启动 Zygote进程还可以执行系统的某个Java类。Android手机常用工具 am 就是一个很好的例子。am 是一个通过发送 Intent 来启动应用程序的工具,但是 am 只是一个包含几行代码的脚本文件,实际上启动还是通过 app_process 来完成的。
am基本用法

  • am start -n 包(package)名/包名.活动(activity)名称
启动虚拟机 AndroidRuntime类
  • Android Runtime类是 Android底层很重要的一个类,它负责启动虚拟机以及Java线程。AndroidRuntime在一个进程中只有一个实例对象,保存在全局变量 gCurRuntime中。
  • AndroidRuntime 构造函数如下(android9.0)

AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
        mExitWithoutCleanup(false),
        mArgBlockStart(argBlockStart),
        mArgBlockLength(argBlockLength)
{
    // 初始化 skia 图形系统;skia是个Google的2D向量图形处理函数库,包含字型、座标转换,以及点阵图都有高效能且简洁的表现。
    SkGraphics::Init();

    // Pre-allocate enough space to hold a fair number of options.
    // 预先分配空间来存放虚拟机的参数
    mOptions.setCapacity(20);
    // 只能初始化一次 gCurRuntime 
    assert(gCurRuntime == NULL);        // one per process
    gCurRuntime = this;
}

AndroidRuntime 的构造函数中,5.0以前会对 skia图形系统进行设置,把底层使用的图形格式设置成 RGB565,一种16位图形格式,16位的图像格式没有24位的色彩丰富,但是能节省内存空间。Android5.0以后去调了这个设置,只保留了 mOptions.setCapacity(20); 来设置虚拟机的参数。
最后 AndroidRuntime 指针被放入了 gCurRuntime 全局指针,但是前面的 main() 函数中AndroidRuntime 是作为一个局部变量 runtime 创建的。这是因为如果启动的 Zygote ,在 ZygoteInit 中会进入等待 Socket 事件循环中,这样布局变量并不会被销毁,如果只执行一个 Java 类,执行结束完就结束了,说 runtime是一个局部变量也没什么问题。

启动虚拟机
	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.");
    }
  • 在main() 函数的结尾,调用了 AndroidRuntime的start函数来执行 Java类。Zygote在运行Java前,还需要初始化整个Java环境,下面看看 start() 函数执行流程。
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    // 打印Log,标志着Android系统已经启动 因为以后的进程都是从 Zygote fork出来的所以不会再执行 start 函数了。
    // 如果Log反复出现下面Log ,则说明系统出错了如果id是zygote 则说明 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_ROOT 中获取,如果没有获取到则默认目录位 /system ;
    // !hasDir("/system") 如果没有/System  则直接 return 退出了
    // 系统目录是在 init 进程中创建出来的
    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 */
    // 启动虚拟机 5.0 以后启动的是 Art虚拟机
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    // 调用虚拟机 onVmCreated onVmCreated类实际上是一个虚函数,调用它实际上调用的是继承类 AndroidRuntime 中的重载函数。下面会贴出来
    onVmCreated(env);

    /*
     * Register android functions. 注册系统 JNI 函数
     * startReg 通过调用 register_jni_procs(gRegJNI, NELEM(gRegJNI), env) 函数将全局数组gRegJNI中的JNI本地函数在虚拟机中注册
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * 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.
     */
    // 下面是准备 Java 类的 main 函数的参数
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    // 先通过 NewObjectArray 创建两个元素的数组
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    // 通过 SetObjectArrayElement 为数组赋值 因为要到Java的世界所以会比较麻烦
    env->SetObjectArrayElement(strArray, 0, classNameStr);
    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        // 先通过 GetStaticMethodID 获取 Java main 函数的id
        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 {
            // 然后通过 CallStaticVoidMethod 来调用 Java 层函数了,至此 Zygote初始化将转入 Java 函数了。
            // 如果不是启动 Zygote 则执行 Java 函数的是 RuntimeInit
            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");
}
  • onVmCreated

onVmCreated会在当前虚拟机环境中根据类名来查找类对象,这表明 app_process 将要调用的Java类对象必须是系统Java类,而不能是随意的应用java类,如果调用普通Java类对象则方式不同。

   virtual void onVmCreated(JNIEnv* env)
    {
        if (mClassName.isEmpty()) {
            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.string());
        mClass = env->FindClass(slashClassName);
        if (mClass == NULL) {
            ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
        }
        free(slashClassName);

        mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
    }
初始化工作 ZygoteInit 类
  • Zygote 的 main 方法做了大概一下几个事情
    :解析调用参数
    :注册 Zygote 的 socket 监听接口,用来接收启动应用程序监听
    :调用 preload() 方法装载系统资源。这样fork出的应用可以包含这些资源了 加快了启动速度。
    :启动 SystemServer 进程
    :调用 runSelectLoo() 方法进入监听和接收消息的循环
    public static void main(String argv[]) {
        ZygoteServer zygoteServer = new ZygoteServer();

        // Mark zygote start. This ensures that thread creation will throw
        // an error.
        ZygoteHooks.startZygoteNoThreadCreation();

        // Zygote goes into its own process group.
        try {
            Os.setpgid(0, 0);
        } catch (ErrnoException ex) {
            throw new RuntimeException("Failed to setpgid(0,0)", ex);
        }

        final Runnable caller;
        try {
            // Report Zygote start time to tron unless it is a runtime restart
            if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
                MetricsLogger.histogram(null, "boot_zygote_init",
                        (int) SystemClock.elapsedRealtime());
            }

            String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
            TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
                    Trace.TRACE_TAG_DALVIK);
            bootTimingsTraceLog.traceBegin("ZygoteInit");
            RuntimeInit.enableDdms();

            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            // 解析调用的参数
            for (int i = 1; i < argv.length; i++) {
                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]);
                }
            }

            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }
            // 注册 Zygote 的 socket 监听接口,用来接收启动应用程序的消息
            zygoteServer.registerServerSocketFromEnv(socketName);
            // In some configurations, we avoid preloading resources and classes eagerly.
            // In such cases, we will preload things prior to our first fork.
            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
                // 装载系统资源,包括系统预加载类,Framework 资源和 openGL 的资源。
                // 这样应用程序被 fork 出来后,应用程序内就已经包含了这些系统资源,大大节省了引用启动时间。
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
            }

            // Do an initial gc to clean up after startup
            bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
            gcAndFinalize();
            bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC

            bootTimingsTraceLog.traceEnd(); // ZygoteInit
            // Disable tracing so that forked processes do not inherit stale tracing tags from
            // Zygote.
            Trace.setTracingEnabled(false, 0);

            Zygote.nativeSecurityInit();

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

            ZygoteHooks.stopZygoteNoThreadCreation();

            if (startSystemServer) {
                // 准备 SystemServer 参数并且 fork 出 SystemServer 进程
                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.
            // 通过调用 runSelectLoop 进入监听和接收消息的环节 里面有一个 while (true) 
            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();
        }
    }

总结一下 Zygote 进程启动过程

  • init 进程 fork 出 zygote 进程
  • 启动虚拟机注册 jni 函数
  • 预加载系统资源
  • 启动 SystemServer
  • 进入 Socket Loop 循环等待消息
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值