【Android面试题】Android Framework核心面试题——Zygote进程的启动流程

Zygote进程的启动流程

这道题想考察什么?

这道题想考察同学对Zygote进程的了解。

考生应该如何回答

zygote进程是由init进程启动的,init进程通过解读init.rc文件的方式启动了zygote,但是zygote所涉及的内容非常多,我们需要一步一步的分析它的细节。

1.Zygote是什么

Zygote是孵化器,在init进程启动时创建的,Zygote通过fork(复制进程)的方式创建android中几乎所有的应用程序进程和SystemServer进程。另外在Zygote进程启动的时候会创建DVM或者ART虚拟机,因此使用fork而建立的应用程序进程和SystemServer进程可以在内部得到一个DVM或者ART的实例副本,这样就保障了每个app进程在Zygote fork的那一刻就有了虚拟机。

2.Zygote启动脚本

init.rc文件中采用了如下所示的Import类型语句来导入Zygote启动脚本:import /init.${ro.zygote}.rc 这里根据属性ro.zygote的内容来导入不同的Zygote启动脚本。从Android 5.0开始,Android开始支持64位程序,Zygote有了32/64位之别,ro.zygote属性的取值有4种:

  • init.zygote32.rc
  • init.zygote64.rc
  • init.zygote64_32.rc

注意:在system/core/rootdir目录中存放上面的Zygote的启动脚本。 上述脚本文件中的部分代码如下:

java 复制代码
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main

意思就是,执行 app_process64,该程序的路径是 /system/bin/app_process64,类名是 main,进程名为 zygote。

3.Zygote进程启动流程

1.上述init脚本实际执行的是 /frameworks/base/cmds/app_process/app_main.cpp 中的 main 方法。 2.启动 zygote 进程在main 方法中会执行。

java 复制代码
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

3.runtime.start 实际执行的是 /frameworks/base/core/jni/AndroidRuntime.cpp 的 start 方法

java 复制代码
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    // 1.启动 java 虚拟机
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }
    onVmCreated(env);

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

    // 3.classNameStr是传入的参数,值为com.android.internall.os.ZygoteInit
    classNameStr = env->NewStringUTF(className);
    // 4.使用toSlashClassName函数将className的 "." 替换为 "/",得到 com/android/internal/os/ZygoteInit
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
    } else {
        // 5.找到 ZygoteInit 中的 main 函数
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
        } else {
            // 6.使用JNI调用ZygoteInit的main函数
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
    }
}

通过上面的代码我们不难发现,Zygote在启动过程中分别做了以下几件事:1)启动java虚拟机;2)注册android SDK需要使用的JNI方法;3)执行ZygoteInit的main函数,进而启动zygote的java层代码,最终Zygote就从Native层进入了Java FrameWork层。

特别说明

启动java虚拟机和 注册JNI的过程是zygote进程存在的一个非常关键的原因。正是因为zygote做了这样的事,因此它被用来fork其他进程。

到目前为止,并没有任何代码进入Java FrameWork层面,因此可以认为,Zygote开创了Java FrameWork层。进入java层代码后,main函数的实现如下所示:

java 复制代码
@UnsupportedAppUsage
    public static void main(String argv[]) {
        // Server socket class for zygote processes.
        ZygoteServer zygoteServer = null; //用来管理和子进程通信的socket服务端 

        // Mark zygote start. This ensures that thread creation will throw
        // an error.

        ZygoteHooks.startZygoteNoThreadCreation(); 	//这里其实只是设置一个标志位,为创建Java线程时做判断处理,如果是zygote进程,则不需要开启线程

        // Zygote goes into its own process group.
        try {
            Os.setpgid(0, 0);  //为zygote进程设置pgid(Process Group ID),详见:`https://stackoverflow.com/questions/41498383/what-do-the-identifiers-pid-ppid-sid-pgid-uid-euid-mean`
        } catch (ErrnoException ex) {
            throw new RuntimeException("Failed to setpgid(0,0)", ex);
        }

        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";//判断当前进程是64位程序还是32位程序,并设置标记
            TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
                    Trace.TRACE_TAG_DALVIK);
            bootTimingsTraceLog.traceBegin("ZygoteInit");
            RuntimeInit.enableDdms();//注册到ddms服务端,内部调用`DdmServer.registerHandler()`

            boolean startSystemServer = false;
            String zygoteSocketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
			//对参数进行解析
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) { //参数重包含`start-system-server`
                    startSystemServer = true; //设置标志为位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)) {
                    zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
                }
            }
			
            final boolean isPrimaryZygote =  zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);//根据socketName判断是否是primaryZygote,可能还有secondZygote

            if (abiList == null) { //如果支持架构为空,直接抛出异常
                throw new RuntimeException("No ABI list supplied.");
            }

            // 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());
                preload(bootTimingsTraceLog); //加载各种系统res资源,类资源
                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(); //调用ZygoteHooks.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.initNativeState(isPrimaryZygote);//jni调用初始化zygote的状态,是否为isPrimaryZygote

            ZygoteHooks.stopZygoteNoThreadCreation(); //结束zygote创建,其实内部是调用`runtime`给`zygote_no_threads_`赋值为false,为创建本地线程做准备

            zygoteServer = new ZygoteServer(isPrimaryZygote); //创建zygoteServer,为其他进程初始化创建时与zygote通信做准备

            if (startSystemServer) { //判断是否需要startSystemServer
                Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);//通过fork的方式开启zygote的子进程,systemServer,并返回一个Runnale对象
                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
                // child (system_server) process.
                if (r != null) {//如果是zygote进程,则r==null,如果不是zygote进程,也就是systemServer进程,则执行下面的代码
                    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); //zygote进程进入死循环中,来获取子进程发送的消息
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            if (zygoteServer != null) {
                zygoteServer.closeServerSocket(); //如果发生异常,则说明zygote初始化失败,zygoteServer也需要关闭
            }
        }

        // We're in the child process and have exited the select loop. Proceed to execute the command.
        if (caller != null) {
            caller.run();
        }
    }

zygote java层的大概流程我们已经梳理完了,现在我们来总结一下:

  1. 基于传参解析对应的zygote.rc脚本,设置进程名为zygote等信息。
  2. 创建ZygoteServer ,这个server存在的目的是让zygote接收来自socket的请求,进而fork进程,zygoteServer里面封装了socket。
  3. preload(bootTimingsTraceLog):加载系统资源res,加载 Android sdk class资源 和其他libc。
  4. forkSystemServer,就是fork 产生了SystemServer进程
  5. 调用runSelectionLoop(),接收其他进程发送的socket消息,进而创建子进程。

特别说明

调用preload完成系统资源的预加载,主要包括preloadClasses,preloadResources,preloadDrawables,preloadSharedLibraries。这些资源并不是给zygote自己实际显示使用的,它之所以存在,是为后面fork 出来的App去运行而准备的。因为app 进程需要运行就需要framework 层的系统资源,而这些资源就会在这里得到加载。

总结

zygote进程的启动分为两大部分,第一部分:执行Native层面的代码,这个过程主要包含:虚拟机启动,JNI资源函数的注册,启动zygote的java层;第二部分:执行java层面的代码,这个过程主要包含:zygote脚本解读,加载公用的各种资源,创建socket服务器并在runSelectionLoop中死循环等待socket消息,fork 了systemServer进程等操作。

详细关注公众号:Android老皮

还能解锁 《Android十大板块文档》 ,让学习更贴近未来实战。已形成PDF版

内容如下

1.Android车载应用开发系统学习指南(附项目实战)
2.Android Framework学习指南,助力成为系统级开发高手
3.2023最新Android中高级面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习路线+项目实战(附源码)
5.Android Jetpack从入门到精通,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提升架构基础
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 性能优化实战+360°全方面性能调优
10.Android零基础入门到精通,高手进阶之路

相关推荐
Java技术小馆2 分钟前
GitDiagram如何让你的GitHub项目可视化
java·后端·面试
UGOTNOSHOT11 分钟前
7.4项目一问题准备
面试
福柯柯21 分钟前
Android ContentProvider的使用
android·contenprovider
不想迷路的小男孩22 分钟前
Android Studio 中Palette跟Component Tree面板消失怎么恢复正常
android·ide·android studio
餐桌上的王子23 分钟前
Android 构建可管理生命周期的应用(一)
android
菠萝加点糖27 分钟前
Android Camera2 + OpenGL离屏渲染示例
android·opengl·camera
用户20187928316738 分钟前
🌟 童话:四大Context徽章诞生记
android
yzpyzp1 小时前
Android studio在点击运行按钮时执行过程中输出的compileDebugKotlin 这个任务是由gradle执行的吗
android·gradle·android studio
aningxiaoxixi1 小时前
安卓之service
android
TeleostNaCl2 小时前
Android 应用开发 | 一种限制拷贝速率解决因 IO 过高导致系统卡顿的方法
android·经验分享