zygote启动过程

作者: 搬砖小老弟 | 来源:发表于2022-05-05 16:13 被阅读0次

    作者:贾东风
    转载地址:https://juejin.cn/post/7093412026483933215

    1. zygote是什么?

    在 Android 系统中,JavaVM(Java 虚拟机)应用程序进程以及运行系统关键服务的 SystemServer 进程都是由 Zygote 来创建的,我们也将它称为 孵化器。它通过 fock (复制进程)的形式来创建 "应用程序进程""SystemServer 进程",由于 Zygote 进程在启动时会创建 JavaVM,因此通过 fock 而创建的 "应用程序进程" 和 "SystemServer 进程" 可以在内部获取一个 JavaVM 的实例拷贝

    2. zygote启动脚本

    zygote的rc脚本是包含在system/core/rootdir/init.rc中的

    import /init.environ.rc
    import /init.usb.rc
    import /init.${ro.hardware}.rc
    import /vendor/etc/init/hw/init.${ro.hardware}.rc
    import /init.usb.configfs.rc
    import /init.${ro.zygote}.rc    // ${ro.zygote} 由厂商定义,与平台相关
    

    init.zygote32.rc init.zygote64.rc 只会启动一个朱进程 init.zygote32_64.rc init.zygote64_32.rc 会分别启动主副zygote进程

    init.zygote32_64.rc:启动两个 zygote 进程 (zygote 和 zygote_secondary),对应的执行程序分别是 app_process32 (主模式)、app_process64

    //init.zygote64_32.rc
    service zygote /system/bin/app_process64 
    -Xzygote /system/bin 
    --zygote 
    --start-system-server 
    --socket-name=zygote
    
    service zygote_secondary /system/bin/app_process32 
    -Xzygote /system/bin 
    --zygote 
    --socket-name=zygote_secondary 
    --enable-lazy-preload
    

    2.1 主要流程:

    1. 在init中通过init进程解析zytoe.rc中的配置执行脚本,通过FindService方法找到rc中配置的zygote服务, fork出zygote进程
    2. 在fork出的zygote进程中,执行service对应的/system/bin/app_process64脚本,进入app_main.cpp对应的main方法

    详情参考: segmentfault.com/a/119000002…

    3. app_main#main

    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;//处于zygote进程中
        bool startSystemServer = false;
        bool application = false;//处于application进程
        String8 niceName;
        String8 className;
        //1\. 解析参数
        ++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;
            }
        }
       //2\. 准备参数
        Vector<String8> args;
        if (!className.isEmpty()) {
            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 {//处于zytote进程模式
            // We're in zygote mode.
            maybeCreateDalvikCache();
            if (startSystemServer) {
                args.add(String8("start-system-server"));
            }
            ...
            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]));
            }
        }
    
        if (!niceName.isEmpty()) {
            runtime.setArgv0(niceName.string(), true /* setProcName */);
        }
        //3\. 根据所处进程的不同,执行不同的初始化
        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.");
        }
    }
    

    3.1 主要流程:

    1. 解析main方法的调用参数,判断当前所处的进程模式
    2. 如果是处于zygote进程,则完成zygote的初始化,并创建systemserver进程
    3. 如果是处于应用进程,则完成运行时初始化

    4. ZygoteInit初始化流程

    4.1 AndroidRuntime.start

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
    {
        ...
    
        //启动虚拟机
        JniInvocation jni_invocation;
        jni_invocation.Init(NULL);
        JNIEnv* env;
        if (startVm(&mJavaVM, &env, zygote) != 0) {
            return;
        }
        onVmCreated(env);
    
        /*
         * 2、Java虚拟机注册JNI方法
         */
        if (startReg(env) < 0) {
            ALOGE("Unable to register all android natives\n");
            return;
        }
    
        ...
        // 3
        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);
        }
    
        /*
         * Start VM.  This thread becomes the main thread of the VM, and will
         * not return until the VM exits.
         */
        // 4
        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 {
            // 6
            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 {
                // 6
                env->CallStaticVoidMethod(startClass, startMeth, strArray);
    
    #if 0
                if (env->ExceptionCheck())
                    threadExitUncaughtException(env);
    #endif
            }
        }
        free(slashClassName);
    
        ...
    }
    

    在main方法中通过runtime.start方法,启动虚拟机,然后通过JNI的方式,调用java 层的ZygoteInit的main方法

    首先,在AndroidRuntime的start函数中,主要处理流程如下:

    1. 使用startVm函数来启动弄Java虚拟机,
    2. 使用startReg函数为Java虚拟机注册JNI方法。

    解析com.android.internal.os.ZygoteInit字符串,通过jni调用该类的main方法,从native进入java世界

    4.2 ZygoteInit.main

    4.2.1 preload

    1. preloadClasses

    PRELOADED_CLASSES = "/system/etc/preloaded-classes";

    使用class.forname在虚拟机中中预加载preloaded-classes

    aosp:/ # cat /system/etc/preloaded-classes
    un.util.logging.LoggingProxy
    sun.util.logging.LoggingSupport
    sun.util.logging.LoggingSupport$1
    sun.util.logging.PlatformLogger
    sun.util.logging.PlatformLogger$1
    sun.util.logging.PlatformLogger$Level
    ....
    
    1. preloadResources
        private static void preloadResources() {
            final VMRuntime runtime = VMRuntime.getRuntime();
    
            try {
                mResources = Resources.getSystem();
                mResources.startPreloading();
                if (PRELOAD_RESOURCES) {
                    ...
                    TypedArray ar = mResources.obtainTypedArray(
                            com.android.internal.R.array.preloaded_drawables);
                    int N = preloadDrawables(ar);
                    ...
                    ar = mResources.obtainTypedArray(
                            com.android.internal.R.array.preloaded_color_state_lists);
                    N = preloadColorStateLists(ar);
                    ...
    
                    if (mResources.getBoolean(
                            com.android.internal.R.bool.config_freeformWindowManagement)) {
                        startTime = SystemClock.uptimeMillis();
                        ar = mResources.obtainTypedArray(
                                com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
                        N = preloadDrawables(ar);
                    ...
                    }
                }
                mResources.finishPreloading();
            } catch (RuntimeException e) {
                Log.w(TAG, "Failure preloading resources", e);
            }
        }
    

    分别调用preloadDrawables,preloadColorStateLists,preloadDrawables预加载各种图像和颜色配置资源

    1. preloadSharedLibraries();
      private static void preloadSharedLibraries() {
            Log.i(TAG, "Preloading shared libraries...");
            System.loadLibrary("android");
            System.loadLibrary("compiler_rt");
            System.loadLibrary("jnigraphics");
    
            try {
                System.loadLibrary("sfplugin_ccodec");
            } catch (Error | RuntimeException e) {
                // tolerate missing sfplugin_ccodec which is only present on Codec 2 devices
            }
        }
    

    4.2.2 createManagedSocketFromInitSocket

    创建socke连接

    4.2.3 runSelectLoop

    主要通过linux poll机制,轮询监听socket客服端请求连接情况。 主要流程:

    1. 将本地socket服务创建的套接字文件描述符添加到被监测的文件描述符socketFDs中去
    2. 调用Os.poll,等待文件描述符就绪
    3. 如果超时,停留一段时间再监听
    4. 如果返回正常的,则判断请求类型,共三中类型:

    4.1 则判断是socket Connect,则通过accept答应请求,创建一个客户端的连接 4.2 如果是闯将子应用请求,则返回创建命令 4.3 如果是断开请求,则断开该文件描述符与server的连接

    关于linux poll机制,请参考linux poll,java层的使用跟native层机制完全一致。

    Runnable runSelectLoop(String abiList) {
            ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
            ArrayList<ZygoteConnection> peers = new ArrayList<>();
            //1\. 将创建socket生成的文件描述符,添加到数组
            socketFDs.add(mZygoteSocket.getFileDescriptor());
            peers.add(null);
    
            mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
    
            while (true) {
                fetchUsapPoolPolicyPropsWithMinInterval();
                mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
    
                int[] usapPipeFDs = null;
                StructPollfd[] pollFDs;
                if (mUsapPoolEnabled) {
                    usapPipeFDs = Zygote.getUsapPipeFDs();
                    pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
                } else {
                    pollFDs = new StructPollfd[socketFDs.size()];
                }
    
                //2\. 初始化poll()的参数fds
                int pollIndex = 0;
                for (FileDescriptor socketFD : socketFDs) {
                    pollFDs[pollIndex] = new StructPollfd();
                    pollFDs[pollIndex].fd = socketFD;
                    pollFDs[pollIndex].events = (short) POLLIN;
                    ++pollIndex;
                }
    
                final int usapPoolEventFDIndex = pollIndex;
    
                 ...
    
                //3\. 阻塞等待请求事件
                int pollReturnValue;
                try {
                    pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
                } catch (ErrnoException ex) {
                    throw new RuntimeException("poll failed", ex);
                }
                //超时
                if (pollReturnValue == 0) {
                    // The poll timeout has been exceeded.  This only occurs when we have finished the
                    // USAP pool refill delay period.
    
                    mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
                    mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
    
                } else {
                    boolean usapPoolFDRead = false;
                    //4\. 轮询遍历检测client[],处理有就绪事件的文件描述符
                    while (--pollIndex >= 0) {
                        if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                            continue;
                        }
    
                        if (pollIndex == 0) {//客户端connect连接请求,非数据发送和读取请求
                            // Zygote server socket
                            ZygoteConnection newPeer = acceptCommandPeer(abiList);//接受客户端连接
                            peers.add(newPeer);
                            socketFDs.add(newPeer.getFileDescriptor());
    
                        } else if (pollIndex < usapPoolEventFDIndex) {
                            // Session socket accepted from the Zygote server socket
    
                            try {
                                ZygoteConnection connection = peers.get(pollIndex);
                                final Runnable command = connection.processOneCommand(this);
    
                                // TODO (chriswailes): Is this extra check necessary?
                                if (mIsForkChild) {//fork 子进程请求
                                    // We're in the child. We should always have a command to run at
                                    // this stage if processOneCommand hasn't called "exec".
                                    if (command == null) {
                                        throw new IllegalStateException("command == null");
                                    }
    
                                    return command;
                                } else {//关闭连接请求
                                    // We're in the server - we should never have any commands to run.
                                    if (command != null) {
                                        throw new IllegalStateException("command != null");
                                    }
    
                                    // We don't know whether the remote side of the socket was closed or
                                    // not until we attempt to read from it from processOneCommand. This
                                    // shows up as a regular POLLIN event in our regular processing
                                    // loop.
                                    if (connection.isClosedByPeer()) {
                                        connection.closeSocket();
                                        peers.remove(pollIndex);
                                        socketFDs.remove(pollIndex);
                                    }
                                }
                            } catch (Exception e) {
                                ...
                            }
                            ...
                        } else {
                           ...
                        }
                    }
    
                    ...
                }
    
                ...
            }
        }
    

    5. 总结

    从以上的分析可以得知,Zygote进程启动中承担的主要职责如下

    1. 创建AppRuntime,执行其start方法。
    2. 创建JVM并为JVM注册JNI方法。
    3. 使用JNI调用ZygoteInit的main函数进入Zygote的Java FrameWork层。
    4. 调用preload完成系统资源的预加载。主要包括preloadClasses,preloadResources,preloadDrawables,preloadSharedLibraries
    5. 通过ZygoteServer创建本地服务端socke连接(LocalServerSocket), 调用Os.socket, bindLocal绑定socket文件和进程
    6. 启动SystemServer进程。
    7. 通过runSelectLoop调用linux poll机制,阻塞等待客户端请求连接,创建应用,关闭连接请求

    相关文章

      网友评论

        本文标题:zygote启动过程

        本文链接:https://www.haomeiwen.com/subject/fpfoyrtx.html