美文网首页
Android系统启动流程(二) —— Zygote进程的启动流

Android系统启动流程(二) —— Zygote进程的启动流

作者: RainMi | 来源:发表于2019-08-14 17:52 被阅读0次

    接上篇: Android系统启动流程(一)init进程的启动流程

    Zygote在英语中是受精卵的意思,从这个名字可以看出,zygote进程是用来孵化其他进程的,SystemServer和其他应用程序进程都是由Zygote进程所创建的。Zygote是以服务的形式存在于Android系统中的,是Android系统的一个重要的守护进程,下面我们通过源码来分析Zygote进程的启动流程。

    1.解析Zygote服务的启动脚本并启动app_main

    在init进程启动时,会解析Zygote服务进程的启动脚本并开启Zygote进程,针对不同位数的操作系统,Zygote也分别对应不同的启动脚本,在Android8.0系统的源码中共有4个启动脚本,分别是init.zygote32.rc(支持32位系统)、init.zygote64.rc(支持64位系统)、init.zygote32_64.rc(同时支持32位和64位,但以32位为主)、init.zygote64_32.rc(同时支持32位和64位,但以64位为主),我们以init.zygote32.rc为例来看一下Zygote服务的脚本源码:
    目录位置:\system\core\rootdir\init.zygote32.rc

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        class main
        priority -20
        user root
        group root readproc
        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
    

    从上面的代码可以看出,该服务的名称是zygote,路径为/system/bin/app_process,参数包含-Xzygote /system/bin --zygote --start-system-server,class的名称为main。

    init进程在解析完上面的代码后,会对zygote服务进行启动,启动部分的脚本代码如下:
    源码位置:\system\core\rootdir\init.rc

    on zygote-start && property:ro.crypto.state=unencrypted  //在.rc文件中,on表示一个触发器,zygote-start是触发器的名称
      //当该触发器被触发后,便会执行下面的命令
        exec_start update_verifier_nonencrypted
        start netd
        start zygote  //启动zygote服务
        start zygote_secondary
    

    上面的代码是定义在init.rc中的一个触发器,当该触发器被触发后,便会执行start zygote这行命令,从而启动zygote服务,start命令对应的函数为do_start,源码如下:
    源码路径:\system\core\init\builtins.cpp

    static int do_start(const std::vector<std::string>& args) {
        // 1.根据service的名称找到该服务
        Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);  
        if (!svc) {
            LOG(ERROR) << "do_start: Service " << args[1] << " not found";
            return -1;
        }
        if (!svc->Start())  //2.调用Start方法开启该服务
            return -1;
        return 0;
    }
    

    在注释1处通过service的名称来找到zygote这个服务的实例,然后再注释2出调用Service的Start方法来开启这个服务,我们来看一下Start方法的源码:
    源码路径:\system\core\init\service.cpp

    bool Service::Start() {
    
        ...
    
        pid_t pid = -1;
        if (namespace_flags_) {
            pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
        } else {
            pid = fork();  // 1.通过fork函数创建zygote子进程
        }
    
        if (pid == 0) {  //pid为0,说明当前在子进程中
            ...
            if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {  // 2.调用execve执行子进程的代码
                PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
            }
    
            _exit(127);
        }
    
        ...
        return true;
    }
    

    在注释1处通过fork函数创建了一个子线程,由于fork函数是对父进程的自我复制,所以fork函数会同时在父进程和子进程中返回,并在父进程中返回子进程的id,在子进程中返回0。

    在注释2处,通过调用execve函数来执行子进程的代码,从zygote的启动脚本中可以看到,该服务的执行代码位于 /system/bin/app_process中,对应的文件为app_main.cpp,这样程序即进入了app_main的main方法中。

    2.通过AppRuntime启动Zygote

    我们先来看一下app_main.cpp的main方法的源码:
    源码路径:\frameworks\base\cmds\app_process\app_main.cpp

    int main(int argc, char* const argv[])
    {
        ...
        AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));//  1.创建AppRuntime实例
    
        ...
    
        while (i < argc) {  // 2.循环遍历参数
            const char* arg = argv[i++];
            if (strcmp(arg, "--zygote") == 0) {  //当参数为“--zygote”时
                zygote = true;  //将zygote标记变为true
                niceName = ZYGOTE_NICE_NAME;
            } else if (strcmp(arg, "--start-system-server") == 0) { //当参数为“--start-system-server”时
                startSystemServer = true;  //将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;
            }
        }
    
        if (zygote) {
            // 3.如果zygote标志为true,则执行runtime的start方法
            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.");
        }
    }
    
    

    在注释1处创建了一个AppRuntime实例。

    在注释2处对参数进行循环遍历,如果参数中含有 "--zygote",则将zygote置为true,如果参数中含有"--start-system-server",则将startSystemServer置为true。

    在注释3处通过调用runtime的start方法来执行ZygoteInit文件中的代码,并将ZygoteInit的文件路径作为参数传入了start方法,这是一个java文件。start函数的源码位于AppRuntime的父类AndroidRuntime中,源码如下:
    源码路径:\frameworks\base\core\jni\AndroidRuntime.cpp

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
    {
        ...
    
        JniInvocation jni_invocation;
        jni_invocation.Init(NULL);//初始化jni
        JNIEnv* env;
        if (startVm(&mJavaVM, &env, zygote) != 0) {  // 1.启动java虚拟机
            return;
        }
        onVmCreated(env);
    
         //主要用于注册jni函数
        if (startReg(env) < 0) {
            ALOGE("Unable to register all android natives\n");
            return;
        }
    
        ...
        /* 2.通过jni的方式执行ZygoteInit的main方法*/
        char* slashClassName = toSlashClassName(className);  //将classname中的"."替换为“/”
        jclass startClass = env->FindClass(slashClassName);//通过jni的方式加载ZygoteInit的java类
        if (startClass == NULL) {
            ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        } else {
            jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                "([Ljava/lang/String;)V");  //找到ZygoteInit的main方法
            if (startMeth == NULL) {
                ALOGE("JavaVM unable to find main() in '%s'\n", className);
            } else {
                env->CallStaticVoidMethod(startClass, startMeth, strArray);//通过jni的方式调用ZygoteInit的mian方法
            ...
        }
        ...
    }
    

    由于ZygoteInit文件是由java代码编写的,因此我们需要用jni的方法来执行ZygoteInit的main方法。

    在注释1处,通过startVm方法创建了java虚拟机。

    在注释2处,通过执行一系列的jni方法,最终调用了ZygoteInit的main方法。

    3.启动SystemServer并持续监听应用创建请求。

    我们来看一下Zygote的main方法的源码:
    源码路径:\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java

    public static void main(String argv[]) {
            ZygoteServer zygoteServer = new ZygoteServer();  //创建ZygoteServer实例
    
            ...
           
            try {
                
                ...
    
                zygoteServer.registerServerSocket(socketName);  // 1.注册ServerSocket
                
                ...
    
                if (startSystemServer) {
                    startSystemServer(abiList, socketName, zygoteServer);  // 2.启动SystemServer
                }
    
                Log.i(TAG, "Accepting command socket connections");
                zygoteServer.runSelectLoop(abiList);  // 3.开启事件循环,不断监听新的请求
    
                zygoteServer.closeServerSocket();
            } catch (Zygote.MethodAndArgsCaller caller) {
                caller.run();
            } catch (Throwable ex) {
                Log.e(TAG, "System zygote died with exception", ex);
                zygoteServer.closeServerSocket();
                throw ex;
            }
        }
    

    在main方法中,首先创建了一个ZygoteServer实例,然后在注释1处,通过调用zygoteServer的registerServerSocket方法对ServerSocket进行了注册。

    在注释2处调用startSystemServer方法开启了SystemServer进程。

    在注释3处通过调用zygoteServer的runSelectLoop方法开启了事件循环,这样zygote进程就可以持续监听新的应用进程创建请求。

    我们先来看一下registerServerSocket方法的源码:
    源码路径:\frameworks\base\core\java\com\android\internal\os\ZygoteServer.java

    private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
    private LocalServerSocket mServerSocket;
    
    void registerServerSocket(String socketName) {
            if (mServerSocket == null) {
                int fileDesc;
                //通过拼接字符串得到最终的Socket名称,最后的结果为“ANDROID_SOCKET_zygote”
                final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
                try {
                    String env = System.getenv(fullSocketName);
                    fileDesc = Integer.parseInt(env);
                } catch (RuntimeException ex) {
                    throw new RuntimeException(fullSocketName + " unset or invalid", ex);
                }
    
                try {
                    FileDescriptor fd = new FileDescriptor();//创建文件描述符对象
                    fd.setInt$(fileDesc);
                    mServerSocket = new LocalServerSocket(fd);//创建ServerSocket对象
                } catch (IOException ex) {
                    throw new RuntimeException(
                            "Error binding to local socket '" + fileDesc + "'", ex);
                }
            }
        }
    

    在registerServerSocket方法中,首先通过字符串拼接的方式获得了Socket的名称,然后根据这个名称创建了一个文件描述符对象。基于linux一切都是文件的思想,socket也被看作是一个文件,该文件描述符即用来表示该socket。

    然后通过这个文件描述符创建了一个LocalServerSocket对象,通过名称我们便可以看出,这是一个运行于服务端的socket,它的作用便是用来监听新的应用进程创建请求。

    我们再来看一下runSelectLoop方法:

    void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
            ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
            ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    
            fds.add(mServerSocket.getFileDescriptor());
            peers.add(null);
    
            /* 1.开始无限循环,不断监听新的请求 */
            while (true) {
                /* 将fds中的数据转移到pollFds数组中 */
                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);
                } 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) {// 2.当数组中没有未执行的任务时
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);//不断进行监听,当有新请求时便会返回
    
                        //将这个连接请求放入数组中
                        peers.add(newPeer);
                        fds.add(newPeer.getFileDesciptor());
                    } else {
                        boolean done = peers.get(i).runOnce(this);// 3.从peers中去除连接请求并执行
                        if (done) {
                            //执行完成后从数组中移除
                            peers.remove(i);
                            fds.remove(i);
                        }
                    }
                }
            }
        }
    

    在runSelectLoop方法中,首先创建了一个FileDescriptor数组和一个ZygoteConnection数组,他们用来存储新接收到的请求。在注释1处开启了一个无限循环来不断监听新的请求,因此zygote进程在android系统的运行过程中会一直存在,直到系统关闭。

    在这个无限循环中,先将fds数组的数据转移到了pollFds数组中,然后对pollFds数组进行了遍历,当i==0时,说明数组中所有请求任务都已经执行完了,那么调用acceptCommandPeer方法来获取新的请求,acceptCommandPeer方法是一个阻塞方法,如果没有新的连接请求,acceptCommandPeer会一直阻塞,直到有新的连接请求到来时,acceptCommandPeer才会将这个新的请求返回。获取到新的请求后便将这个请求放入peers和fds数组中。

    当i不为0时,说明数组中还存在未执行的请求,则将请求取出并调用runOnce方法来执行这个请求。

    我们先来看一下acceptCommandPeer方法的源码:

    private ZygoteConnection acceptCommandPeer(String abiList) {
            try {
                return createNewConnection(mServerSocket.accept(), abiList);//调用accept方法等待新的请求
            } catch (IOException ex) {
                throw new RuntimeException(
                        "IOException during accept()", ex);
            }
        }
    
        protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
                throws IOException {
            return new ZygoteConnection(socket, abiList);  //创建ZygoteConnection实例
        }
    

    acceptCommandPeer的源码非常简单,就是就是调用accept方法等待新的请求,该方法会一直阻塞当前线程,直到有新的请求到来。

    我们再来看一下ZygoteConnection的runOnce方法:
    源码路径:\frameworks\base\core\java\com\android\internal\os\ZygoteConnection.java

    boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
    
                ...          
    
                pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                        parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                        parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
                        parsedArgs.appDataDir);
    
           ...
    
        }
    

    runOnce方法的代码很长,我们只看最关键的一句代码,即调用forkAndSpecialize方法创建新的进程,该方法最终会调用native方法来fork新的应用程序进程。

    之前我们讲过,zygote进程在启动的时候会创建一个java虚拟机,而我们的应用程序进程都是由zygote进程fork得来的,而fork的本质是对父进程的自我复制,因此所有的应用程序子进程也会获得一个复制而来的java虚拟机副本,这样便无需在应用程序进程中单独启动java虚拟机了。

    相关文章

      网友评论

          本文标题:Android系统启动流程(二) —— Zygote进程的启动流

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