Android 進階解密:系統啓動流程(二)解析Zygote進程啓動過程

上一篇文章咱們分析了init進程的啓動過程,啓動過程當中主要作了三件事,其中一件就是建立了Zygote進程,那麼Zygote進程是什麼,它作了哪些事呢?java

1、Zygote簡介

在Android系統中,DVM(Dalvik虛擬機)、應用程序進程以及運行系統的關鍵服務的SystemServer進程都是由Zygote進程來建立的,咱們也將它稱爲孵化器。它經過fork (複製進程)的形式來建立應用程序進程和SystemServer進程,因爲Zygote進程在啓動時會建立DVM,所以經過fork而建立的應用程序進程和SystemServer進程能夠在內部獲取一個DVM的實例拷貝。android

2、AppRuntime分析

咱們從上篇文章得知init啓動zygote時主要是調用app_main.cpp的main函數中的AppRuntime的start來啓動zygote進程的,咱們就從app_main.cpp的main函數開始分析,以下所示。bash

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函數:app

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);

    /*
     * 二、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層。socket

接着,咱們看看Zygoteinit.java中的main方法:ide

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進程去建立新的應用程序進程。oop

接着,咱們回到ZygoteInit的main方法,在註釋2處會預加載類和資源。而後在註釋3處,使用了forkSystemServer()方法去建立SystemServer進程。forkSystemServer()方法核心代碼以下所示:post

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);

    // 二、無限循環等待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進程。
相關文章
相關標籤/搜索