zygote启动过程

636 阅读4分钟

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…

1. zygote#1# main进入流程.png

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初始化流程

zygote#2#初始化.png

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机制,阻塞等待客户端请求连接,创建应用,关闭连接请求

引用: jsonchao.github.io/2019/02/24/… cloud.tencent.com/developer/a… segmentfault.com/a/119000002…