Android 进阶解密笔记:系统启动流程(二)解析Zygote进程启动过程

Android 进阶解密笔记:系统启动流程(二)解析Zygote进程启动过程

Android小彩虹2021-07-17 11:00:492920A+A-

上一篇文章我们分析了init进程的启动过程,启动过程中主要做了三件事,其中一件就是创建了Zygote进程,那么Zygote进程是什么,它做了哪些事呢?

一、Zygote简介

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

二、AppRuntime分析

我们从上篇文章得知init启动zygote时主要是调用app_main.cpp的main函数中的AppRuntime的start来启动zygote进程的,我们就从app_main.cpp的main函数开始分析,如下所示。

int main(int argc, char* const argv[])
{
    ...
    while (i < argc) {
        const char* arg = argv[i++];
        // 1
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            // 2
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            // 3
            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;
        }
    }

    ...

    // 4
    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.");
    }
}

由前可知,Zygote进程都是通过fork自身来创建子进程的,这样Zygote进程和由它fork出来的子进程都会进入app_main.cpp的main函数中,所以在mian函数中,首先会判断当前运行在哪个进程,在注释1处,会判断参数arg中释放包含了”–zygote”,如果包含了,则说明main函数是运行在Zygote进程中的并会将zygote标记置为true。在注释2处会判断参数arg中是否包含了”–start-system-server”,如果包含了则表示当前是处在SystemServer进程中并将startSystemServer设置为true。同理在注释3处会判断参数arg是否包含”–application”,如果包含了说明当前处在应用程序进程中并将application标记置为true。最后在注释4处,当zygote标志是true的时候,也就是当前正处在Zygote进程中时,则使用AppRuntime.start()函数启动Zygote进程。

我们接着看看AndroidRuntime的start函数:

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ...

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    // 1
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * 2、Register android functions.
     */
    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);

    ...
}

首先,在AndroidRuntime的start函数中,会现在注释1处使用startVm函数来启动弄Java虚拟机,然后在注释2处使用startReg函数为Java虚拟机注册JNI方法。在注释3处的classNameStr是传入的参数,值为com.android.internall.os.ZygoteInit。然后在注释4处使用toSlashClassName函数将className的”.”替换为”/“,替换后的值为com/android/internal/os/ZygoteInit。接着根据这个值找到ZygoteInit并在注释5处找到ZygoteInit的main函数,最后在注释6处使用JNI调用ZygoteInit的main函数,之所以这里要使用JNI,是因为ZygoteInit是java代码。最终,Zygote就从Native层进入了Java FrameWork层。在此之前,并没有任何代码进入Java FrameWork层面,因此可以认为,Zygote开创了java FrameWork层。

接着,我们看看Zygoteinit.java中的main方法:

public static void main(String argv[]) {

    ...

    try {
        ...

        // 1
        zygoteServer.registerServerSocketFromEnv(socketName);
        // 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());

            // 2
            preload(bootTimingsTraceLog);
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                SystemClock.uptimeMillis());
            bootTimingsTraceLog.traceEnd(); // ZygotePreload
        } else {
            Zygote.resetNicePriority();
        }

        ...

        if (startSystemServer) {
            // 3
            Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

            // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
            // child (system_server) process.
            if (r != null) {
                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.
        // 4
        caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        throw ex;
    } finally {
        zygoteServer.closeServerSocket();
    }

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

首先,在注释1处调用了ZygoteServer的registerServerSocketFromEnv方法创建了一个名为”zygote”的Server端的Socket,它用来等待ActivityManagerService请求Zygote来创建新的应用程序进程。我首先分析下registerServerSocketFromEnv方法的处理逻辑,源码如下所示:

private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";

void registerServerSocketFromEnv(String socketName) {
    if (mServerSocket == null) {
        int fileDesc;
        // 1
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
        try {
            // 2
            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);
            // 3
            mServerSocket = new LocalServerSocket(fd);
            mCloseSocketFd = true;
        } catch (IOException ex) {
            throw new RuntimeException(
                    "Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}

首先,会在注释1处将Socket的名字拼接为“ANDROID_SOCKET_zygote“,在注释2处调用System.getenv()方法得到该Socket对应的环境变量中的值,然后将这个Socket环境变量值解析为int类型的文件描述符参数。接着,在注释4处,使用上面得到的文件描述符参数得到一个文件描述符,并由此新建一个服务端Socket。当Zygote进程将SystemServer进程启动红藕,就会在这个服务端Socket上等待AMS请求Zygote进程去创建新的应用程序进程。

接着,我们回到ZygoteInit的main方法,在注释2处会预加载类和资源。然后在注释3处,使用了forkSystemServer()方法去创建SystemServer进程。forkSystemServer()方法核心代码如下所示:

 private static Runnable forkSystemServer(String abiList, String socketName,
        ZygoteServer zygoteServer) {

    // 一系统创建SystemServer进程所需参数的准备工作

    try {
        ...

        /* Request to fork the system server process */
        // 3.1
        pid = Zygote.forkSystemServer(
                parsedArgs.uid, parsedArgs.gid,
                parsedArgs.gids,
                parsedArgs.runtimeFlags,
                null,
                parsedArgs.permittedCapabilities,
                parsedArgs.effectiveCapabilities);
    } catch (IllegalArgumentException ex) {
        throw new RuntimeException(ex);
    }

    /* For child process */
    if (pid == 0) {
        if (hasSecondZygote(abiList)) {
            waitForSecondaryZygote(socketName);
        }

        zygoteServer.closeServerSocket();

        // 3.2
        return handleSystemServerProcess(parsedArgs);
    }

    return null;
}

可以看到,forkSystemServer()方法中,注释3.1调用了Zygote的forkSystemServer()方法去创建SystemServer进程,其内部会执行nativeForkSystemServer这个Native方法,它最终会使用fork函数在当前进程创建一个SystemServer进程。如果pid等于0,即当前是处于新创建的子进程ServerServer进程中,则在注释3.2处使用handleSystemServerProcess()方法处理SystemServer进程的一些处理工作。

我们再回到Zygoteinit.java中main方法中的注释4处,这里调用了ZygoteServer的runSelectLoop方法来等等ActivityManagerService来请求创建新的应用程序进程,runSelectLoop()方法如下所示:

Runnable runSelectLoop(String abiList) {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

    // 1
    fds.add(mServerSocket.getFileDescriptor());
    peers.add(null);

    // 2、无限循环等待AMS请求创建应用程序进程
    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);
        } catch (ErrnoException ex) {
            throw new RuntimeException("poll failed", ex);
        }
        // 3
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if ((pollFds[i].revents & POLLIN) == 0) {
                continue;
            }

            // 4
            if (i == 0) {
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                try {
                    ZygoteConnection connection = peers.get(i);

                    // 5
                    final Runnable command = connection.processOneCommand(this);

                    if (mIsForkChild) {
                        // 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(i);
                            fds.remove(i);
                        }
                    }
                } catch (Exception e) {
                    if (!mIsForkChild) {
                        // We're in the server so any exception here is one that has taken place // pre-fork while processing commands or reading / writing from the // control socket. Make a loud noise about any such exceptions so that // we know exactly what failed and why. Slog.e(TAG, "Exception executing zygote command: ", e); // Make sure the socket is closed so that the other end knows immediately // that something has gone wrong and doesn't time out waiting for a
                        // response.
                        ZygoteConnection conn = peers.remove(i);
                        conn.closeSocket();

                        fds.remove(i);
                    } else {
                        // We're in the child so any exception caught here has happened post // fork and before we execute ActivityThread.main (or any other main() // method). Log the details of the exception and bring down the process. Log.e(TAG, "Caught post-fork exception in child process.", e); throw e; } } finally { // Reset the child flag, in the event that the child process is a child- // zygote. The flag will not be consulted this loop pass after the Runnable // is returned. mIsForkChild = false; } } } } } 

首先,在注释1处,会调用服务端的mServerSocket的getFileDescriptor()函数来去获得自身的fd字段值并加入fds列表中。然后,在注释2处,无限循环用来等待AMS请求Zygote进程创建新的应用程序进程。在注释3处会遍历pollFds这个fd列表,如果i等于0,则说明服务端Socket与客户端连接上了,即当前Zygote进程与AMS进程建立了连接。接着,在注释4处调用acceptCommandPeer()方法得到ZygoteConnection对象,并将其加入peers列表中。如果i不等于0,则表明AMS想Zygote进程发送了一个创建应用程序进程的请求,最后会在注释5处执行ZygoteConnection.runOnce方法去创建一个新的应用程序进程。

总结

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

  1. 创建AppRuntime,执行其start方法,启动Zygote进程。。
  2. 创建JVM并为JVM注册JNI方法。
  3. 使用JNI调用ZygoteInit的main函数进入Zygote的Java FrameWork层。
  4. 使用registerZygoteSocket方法创建服务器端Socket,并通过runSelectLoop方法等等AMS的请求去创建新的应用进程。
  5. 启动SystemServer进程。

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们