美文网首页
Zygote工作流程

Zygote工作流程

作者: 小城哇哇 | 来源:发表于2022-05-10 20:36 被阅读0次

    Zygote的作用

    1)启动SystemServer(使用zygote的资源:常用类,JNI函数,主题资源,共享库)
    2)孵化app应用进程

    如何启动的

    1)通过init进程,init进程是系统启动后用户空间第一个进程,它通过读取init.rc读取那些系统服务需要启动,如zygote, service manager, 启动是通过fork() + execve()做的。

    fork+execve
    pid_t pid = fork();
    if(pid == 0){
        //child process
        execve(path, argv, env);
    }else{
        //parent process
    }
    
    

    2)子进程会继承父进程的资源,但是如果调用execve()执行了新程序,那继承的资源就会被清掉,被新的二进制程序替换掉。

    3)启动后的流程 native层:zygote启动后,执行了execve系统调用,执行了c++的二进制程序,有main函数做入口,虽然zygote天生就是native的,做好准备工作后,就切到java世界运行了。 java层:

    int main(int argc, char *argv[]){
        javaVM *jvm;
        JNIEnv *env;
        JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);  //创建虚拟机
        jclass clazz = env->FindClass("ZygoteInit");    //找到zygoteinit java类
        jmethodID method = env->GetStaticMethodID(clazz, "Main", "([java/lang/String;)V"); //找到里面的main函数
        env->CallStaticVoidMethod(clazz, method, args); //运行main函数
        jvm->DestroyJavaVM();
    }
    
    

    整理流程为:
    1)启动Android虚拟机
    2)注册Android的JNI函数
    3)通过JNI调用,进入JAVA世界

    ZygoteInit的main方法

    public static void main(String argv[]) {
        //zygote服务类 
        ZygoteServer zygoteServer = new ZygoteServer();
        //不允许创建新的线程否则会报错
        ZygoteHooks.startZygoteNoThreadCreation();
        //绑定进程
        try {
            Os.setpgid(0, 0);
        } catch (ErrnoException ex) {
            throw new RuntimeException("Failed to setpgid(0,0)", ex);
        }
        final Runnable caller;
        try {
            ....
            省略一些log输出
            ....
            //注册ddms
            RuntimeInit.enableDdms();
            //标识是否要启动系统服务
            boolean startSystemServer = false;
            //socket名称
            String socketName = "zygote";
            String abiList = null; // abi列表(armeabi、armeabi-v7a、x86等)
            //标识是否要预加载资源
            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());//获取abi
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());//获取socket名称
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
                }
            }
            if (abiList == null) {
                //异常情况
                throw new RuntimeException("No ABI list supplied.");
            }
            zygoteServer.registerServerSocketFromEnv(socketName); //注册socket
            if (!enableLazyPreload) {
                //启动预加载
                ....
                preload(bootTimingsTraceLog);
                ....
            } else {
                //降低进程优先级
                Zygote.resetNicePriority();
            }
            ...
            gcAndFinalize(); //进行一次gc
            ...
            //初始化native访问安全
            Zygote.nativeSecurityInit();
            //卸载storage空间
            Zygote.nativeUnmountStorageOnInit();
            //通知虚拟机可以创建线程了
            ZygoteHooks.stopZygoteNoThreadCreation();
            //启动系统服务
            if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
                //上文提到过fork进程会有两次回调,这里如果r==null则是Zygote进程,否则是SystemServer进程
                if (r != null) {
                    r.run();
                    return;//SystemServer进程就直接返回了
                }
            }
            //父进程中会执行到这里,轮询Socket发的命令
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            zygoteServer.closeServerSocket();//子进程断开与Socket的连接
        }
        //执行子进程
        if (caller != null) {
            caller.run();
        }
    }
    
    

    main方法总结:
    整体流程主要涉及获取c++调用层传递过来的参数,包括是否启动系统服务、是否预加载系统资源、获取abi和绑定socket名称。在进程创建开始前禁止创建新的线程,做好初始化之后才允许创建新的线程,最后绑定Socket进行轮询。

    preload方法

    static void preload(TimingsTraceLog bootTimingsTraceLog) {
        ....
        preloadClasses(); //Class.forName方式预加载Class
        ....
        preloadResources();//预加载系统资源resource,如drawable、color
        ....
        preloadOpenGL(); //加载openGL
        ....
        preloadSharedLibraries(); //System.loadLibrary方式加载一些公共库
        preloadTextResources();  //加载Text资源、字体等
        WebViewFactory.prepareWebViewInZygote(); //WebViewFactory在Zygote初始化
        endIcuCachePinning();
        warmUpJcaProviders();
        sPreloadComplete = true; //预加载完成标记
    }
    
    

    preload方法总结:

    preloadClassess 将framework.jar里的preloaded-classes 定义的所有class load到内存里,preloaded-classes 编译Android后可以在framework/base下找到。而preloadResources 将系统的Resource(不是在用户apk里定义的resource)load到内存。资源preload到Zygoted的进程地址空间, 所有fork的子进程将共享这份空间而无需重新load, 这大大减少了应用程序的启动时间,但反过来增加了系统的启动时间。通过对preload 类和资源数目进行调整可以加快系统启动。Preload也是Android启动最耗时的部分之一。

    forkSystemServer方法

    private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        ... 省略一些参数
        //启动SystemServer命令
        String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
            "com.android.server.SystemServer", //这个参数代表着系统进程fork成功后回执行SystemServer中的main方法
        };
        //处理参数信息
        ....
        int pid;
        try {
            ....
            处理参数信息
            ....
            //fork系统服务
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.runtimeFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }
        if (pid == 0) {
            //这里是子进程的回调
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }
            zygoteServer.closeServerSocket(); //子进程并不需要保持zygote socket的连接了,所以进行断开操作
            return handleSystemServerProcess(parsedArgs); //开始处理系统服务
        }
        return null;
    }
    
    

    handleSystemServerProcess方法最终的核心是调用ZygoteInit.zygoteInit

    public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
        //初始化log
        RuntimeInit.redirectLogStreams();
        //初始化log打印和线程崩溃回调
        RuntimeInit.commonInit();
        ZygoteInit.nativeZygoteInit();
        //RuntimeInit类去执行到SystemServer的main方法
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
    }
    
    protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
            ClassLoader classLoader) {
        ....
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }
    
    protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        Class<?> cl;
        ....
            cl = Class.forName(className, true, classLoader);
        ....
        Method m;
        ....
            m = cl.getMethod("main", new Class[] { String[].class });
        ....
        return new MethodAndArgsCaller(m, argv); //执行到SystemServer的main方法
    }
    
    

    ZygoteServer

    ZygoteServer是Zygote用来与Socket建立连接的,主要涉及registerServerSocketFromEnvrunSelectLoop

    registerServerSocketFromEnv方法

    void registerServerSocketFromEnv(String socketName) {
        if (mServerSocket == null) {
            int fileDesc;
            final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;//socket全名
            try {
                String env = System.getenv(fullSocketName);
                fileDesc = Integer.parseInt(env); //socket句柄(int值)
            } catch (RuntimeException ex) {
                throw new RuntimeException(fullSocketName + " unset or invalid", ex);
            }
            try {
                FileDescriptor fd = new FileDescriptor();
                fd.setInt$(fileDesc);
                mServerSocket = new LocalServerSocket(fd);//通过LocalServerSocket进行监听
                mCloseSocketFd = true;
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex);
            }
        }
    }
    
    

    runSelectLoop

    Runnable runSelectLoop(String abiList) {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
        fds.add(mServerSocket.getFileDescriptor());
        peers.add(null);
        //开启一个死循环从socket中获取数据
        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);//系统的poll机制
            } 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 {
                    try {
                        ZygoteConnection connection = peers.get(i);//每一次循环都是取出来ZygoteConnection
                        final Runnable command = connection.processOneCommand(this); //processOneCommand方法fork出进程,App的进程就是通过AMS向Zygote发送socket消息,通过这里经过ZygoteConnection执行的
                        if (mIsForkChild) {
                            if (command == null) {
                                throw new IllegalStateException("command == null");
                            }
    
                            return command;
                        } else {
                            ....
                            if (connection.isClosedByPeer()) {
                                connection.closeSocket();
                                peers.remove(i);
                                fds.remove(i);
                            }
                        }
                        ....
    }
    
    

    runSelectLoop方法通过轮询的方式不断从socket中取出消息,来完成进程的创建。AMS通过向Zygote绑定的socket中发送消息来请求Zygote来fork出app的进程。

    zygote启动过程的2个细节:

    1)zygote在fork时要保证单线程, 因为不管父进程有多少个线程,子进程创建时只有一个线程,多的线程就不见了,会导致很多奇怪的问题:子进程死锁,状态不一致,所以不如直接,fork子进程时,停掉其他线程,创建完了子进程再重启那些线程,zygote就是这么做的,它不只有主线程,还有与虚拟机相关的守护线程。
    2)zygote的IPC没有采用binder机制, 它采用的是socket,所以应用的binder机制不是从zygote继承的,而是AP进程创建后自己启动的binder机。

    1.孵化AP问啥不交给systemServer,而是专门设计一个zygote?
    应用在启动的时候,需要做很多准备工作,如启动虚拟机,加载各个类系统资源,都非常耗时, 如果zygote把init工作做好,再在fork时共享给子进程,那效率就非常高。 这就是zygote存在的价值,systemServer不能做,因为它跑了一堆系统服务,他们不能被继承AP进程。 而且AP启动时,内存空间除了必要的资源外,最好是干净的,不要继承一堆乱七八糟的东西, 所以,不如给systemServer和AP进程都要用的资源抽出来单独放在一个进程里,这就是zygote进程.

    2,zyogte的IPC为什么不用binder?用binder会有问题吗?
    不用binder有个原因:
    1)如果用了binder,zygote要先启动binder机制,打开binder驱动,获得描述符,mmap进程内存映射,注册binder线程,还要创建一个binder对象注册到serviceManager,另外AMS要想zygote发起创建应用进程请求的话,要先从serviceManager查询zygote的binder对象,再发起binder调用,非常繁琐。 相比之下,zygote和systemserver本就是父子关系,对于简单的消息通信,用管道或者socket非常方便。
    2)如果zygote用了binder机制,再fork systemServer,那systemServer就继承了zygote的描述符和映射的内存,这两个进程在binder驱动层就会共用一套数据结构,这肯定是不行的。那还得把旧的描述符关掉,再重新启动一遍binder机制,自找麻烦。

    相关文章

      网友评论

          本文标题:Zygote工作流程

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