android zygote 進程分析

1. 概述

在 Android 系統中,所有的應用程序以及系統服務進程 SystemService 都是由 zygote 進程孕育 (fork)出來的。zygote 進程的作用可以概括爲以下三點:

  1. 創建 java 虛擬機,加載系統資源
  2. 應用程序啓動過程中負責 fork 出子進程
    在 Android 應用程序啓動時,ActivityManagerService 會通過 socket 與 zygote 進程進行通信,請求它 fork 一個子進程出來作爲這個即將要啓動的應用程序的進程。
  3. 系統重要服務進程 SystemService 的孕育者
    Android 系統中非常重要的系統服務進程 SystemServer 也是 zygote 在啓動過程中 fork 出來的。

2. zygote 分析

2.1 啓動過程分析

我們知道 Android 系統是基於 Linux 內核的,而在 Linux 系統中,所有的進程都是 init 進程的子進程,zygote 進程也不例外,它是由 init 進程創建的。

File:system/core/rootdir/init.rc

import /init.${ro.zygote}.rc
File: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

  1. 關鍵字 service 爲:創建一個名爲 zygote 的進程,這個 zygote 進程要執行的程序是 /system/bin/app_process,後面是傳給 app_process 的參數。
  2. socket 關鍵字表示這個 zygote 進程需要一個名稱爲 zygote 的 socket 資源,這樣系統在我們就可以在 /dev/socket 目錄下看到一個名爲 zygote 的文件,這裏的 socket 主要用於本地進程間通信。ActivityManagerService 就是通這個 socket 來和 zygote 進程通信請求 fork 一個應用程序進程。
  3. 後面的 onrestart 表示這個 zygote 進程重啓時需要執行的命令。

而進程 app_process 的源碼位置在:platform/frameworks/base/cmds/app_process/app_main.cpp

這個函數由兩種啓動模式:

  • 一種是 zygote 模式,也就是初始化 zygote 進程,傳遞的參數有 --start-system-server、 --socket-name=zygote,前者表示啓動SystemServer,後者指定socket的名稱
  • 一種是 application 模式,也就是啓動普通應用程序,傳遞的參數有 class 名字以及 class 帶的參數

兩者最終都是調用 AppRuntime 對象的 start 函數,加載 ZygoteInit 或 RuntimeInit 兩個Java類,並將之前整理的參數傳入進去。

由於本篇講的是zygote進程啓動流程,因此接下來我只講解ZygoteInit的加載.

int main(int argc, char* const argv[])
{
    ...

    // 構建 AppRuntime 對象,並將參數 (-Xzygote) 傳遞進去
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;
    
    // 所有在"--"後面或者非"-"開頭的參數都會被傳入vm
    
    // 所謂一個特例,在 spaced_commands 變量中定義的參數會被傳入vm
    const char* spaced_commands[] = { "-cp", "-classpath" };
    // Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
    bool known_command = false;
    
    ...
    省略代碼
    通過 runtime.addOption 初始化參數集合
    這邊直接跳過
    ...

     // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    // 跳過一個參數,之前跳過了 -Xzygote,這裏繼續跳過 /system/bin,就是所謂的 parent dir
    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) { // 表示是 zygote 啓動模式
            zygote = true;
            niceName = ZYGOTE_NICE_NAME; // 根據平臺可能是 zygote64 或 zygote
        } else if (strcmp(arg, "--start-system-server") == 0) { // 是否開啓 SystemService
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) { // application 啓動模式
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) { // 進程別名
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) { // application啓動的class
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        // className 不爲空表示是 application 啓動模式
        // 這裏不做分析
        ...
    } else {
        // 表示處在 zygote 啓動模式
        // 新建Dalvik的緩存目錄.
        maybeCreateDalvikCache();

        if (startSystemServer) {
            args.add(String8("start-system-server"));
        }

        //加入--abi-list=參數
        char prop[PROP_VALUE_MAX];
        if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
            LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
                ABI_LIST_PROPERTY);
            return 11;
        }

        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 */);
    }

    if (zygote) { 
        //如果是zygote啓動模式,則加載ZygoteInit
        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.");
    }

我們看到,在最後調用的是 runtime.start 函數,這個就是要啓動虛擬機了,接下來我們分析 start 函數

2.2 AppRuntime 分析

AppRuntime 類的聲明和實現均在 app_main.cpp 中,它是由 AndroidRuntime 類派生出來的,AppRuntime 重載了 onStart、onZygoteInit、onExit 等一些重要的方法。

前面調用的 start 方法實現在基類 AndroidRuntime 中,前半部分主要是初始化JNI,然後創建虛擬機,註冊一些JNI函數

File:platform/frameworks/base/core/jni/AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ... // 日誌打印,獲取ANDROID_ROOT環境變量

    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    // ① 創建虛擬機
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env); //表示虛擬創建完成時回調的方法
    
    /*
     * Register android functions.
     */
     // ② 註冊 JNI 函數
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    
    ... //③ JNI 方式調用 ZygoteInit 類的 main 函數
}

上面的分析中,關鍵點①、②、③共同組成了 Android 系統中 Java 世界的三部曲。

1. 創建虛擬機 startVm

這個函數沒有特別之處,就是調用JNI的虛擬機創建函數,但是創建虛擬機時的一些參數是在 startVm 中確定的,其實就是從各種系統屬性中讀取一些參數,然後通過 addOption 設置到 AndroidRuntime 的 mOptions 數組中存起來,代碼如下:

File:platform/frameworks/base/core/jni/AndroidRuntime.cpp

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{

    // 調用 JNI_CreateJavaVM 創建虛擬機,pEnv 返回當前線程的 JniEnv 變量
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        return -1;
    }

    return 0;
}

2. 註冊 JNI 函數 startReg

前面是如何創建虛擬機,下一步需要給虛擬機註冊一些 JNI 函數,正式因爲後續 Java 世界用到的一些函數時採用 native 方法實現的,所以才必須提前註冊這些函數

File:platform/frameworks/base/core/jni/AndroidRuntime.cpp

/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    ATRACE_NAME("RegisterAndroidNatives");
    
    // 設置 Thread 類的線程創建函數爲 javaCreateThreadEtc
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);

    ALOGV("--- registering native functions ---\n");

    // 創建一個 200 容量的局部引用作用域
    env->PushLocalFrame(200);

    // 註冊 Jni 函數
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL); //釋放局部引用作用域

    //createJavaThread("fubar", quickTest, (void*) "hello");

    return 0;
}

register_jni_procs是主要的 JNI 註冊函數

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {
#ifndef NDEBUG
            ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
            return -1;
        }
    }
    return 0;
}

mProc 就是爲 Java 類註冊了 JNI 函數

3. 反射調用 ZygoteInit 類的 main 函數

虛擬機已創建好,JNI函數也已註冊,下一步就是通過CallStaticVoidMethod了,通過這個函數真正進入 Java 世界。

這個 Java 世界的入口就是 CallStaticVoidMethod 最終調用的 com.android.internal.os.ZygoteInit 的 main 函數。

public static void main(String argv[]) {
    ZygoteServer zygoteServer = new ZygoteServer();
    
    ...
    
    // ① 創建 Socket,通過這個 Socket 與 ActivityManagerService 通信
    zygoteServer.registerServerSocket(socketName);
    ...
    // ② 預加載資源
    preload(bootTimingsTraceLog);
    ...
    
    if (startSystemServer) {
        // ③ fork 出 SystemService
        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;
        }
    }
    
    ......
    zygoteServer.runSelectLoop(abiList);
    ......
}   

建立 IPC 通信服務端 - registerServerSocket

先分析主體的框架,首先創建了一個 ZygoteServer 類,並 registerServerSocket 創建一個 Socket,這個Socket 將 listen 並 accept Client

它將作爲服務端來和客戶端的 ActivityManagerService 進行通信來 fork 出子進程。

預加載類和資源 - preload

首先看 preloadClass

File:ZygoteInit.java

private static void preloadClasses() {
        final VMRuntime runtime = VMRuntime.getRuntime();

        InputStream is;
        try {
            // 預加載類的信息存儲在 PRELOADED_CLASSED 中(/system/etc/preloaded-classes)
            is = new FileInputStream(PRELOADED_CLASSES);
        } catch (FileNotFoundException e) {
            Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
            return;
        }

        ... // 統計工作
        
        try {
            BufferedReader br
                = new BufferedReader(new InputStreamReader(is), 256);

            int count = 0;
            String line;
            while ((line = br.readLine()) != null) {
                // Skip comments and blank lines.
                line = line.trim();
                if (line.startsWith("#") || line.equals("")) {
                    continue;
                }

                Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
                try {
                    if (false) {
                        Log.v(TAG, "Preloading " + line + "...");
                    }
                    // Load and explicitly initialize the given class. Use
                    // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
                    // (to derive the caller's class-loader). Use true to force initialization, and
                    // null for the boot classpath class-loader (could as well cache the
                    
                    // 通過 JAVA 反射來加載類
                    // class-loader of this class in a variable).
                    Class.forName(line, true, null);
                    count++;
                } catch (ClassNotFoundException e) {
                }
            }

            Log.i(TAG, "...preloaded " + count + " classes in "
                    + (SystemClock.uptimeMillis()-startTime) + "ms.");
        } catch (IOException e) {
            Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
        } finally {
           ... // 掃尾工作
        }
    }

preload_class 文件由 framework/base/tools/preload 工具生成,它判斷每個類加載的時間是否大於1250微秒,超過就寫入 preload_class 文件,最後由 zygote 進行預加載。

preloadClass 執行的時間比較長,這也是andorid啓動慢的原因,可以在此進行開機速度優化

preloadResource 預加載的原理很簡單,就是在 zygote 進程啓動後將資源讀取出來,保存到 Resources 一個全局靜態變量中,下次讀取系統資源的時候優先從靜態變量中查找

啓動 SystemService —— forkSystemServer

第三個關鍵點是 startSystemService,這個函數會創建 Java 世界中傳統 Service 所駐留的進程 system_service,該進程是 framework 的核心。

 private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        ...
        
        // 設置參數
        /* Hardcoded command line to start the system server */
        String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "com.android.server.SystemServer",
        };
        ZygoteConnection.Arguments parsedArgs = null;

        int pid;

        try {
            parsedArgs = new ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

            /* Request to fork the system server process */
            // fork 出一個子進程,這個子進程就是 systemSevice
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.debugFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }

        /* For child process */
        if (pid == 0) {
            // fork 成功,開啓一些預備工作
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }

        return null;
    }

Zygote.forkSystemServer() 創建一個系統服務進程,進程創建成功後返回 pid 爲 0,由於此處生成的新進程和 Zygote 進程一模一樣,也就是說這個新進程中同樣包含了剛纔創建的 Socket,但是該 Socket 在此處無效,因此要將其關閉。接下來調用 handleSystemServerProcess() 處理剛纔新建的進程即 SystemServer 進程,需要注意此時已經工作在 SystemServe r進程中

有求必應之等待請求 —— runSelectLoop

前面一個關鍵點是 registZygotSocket 註冊了一個用於 IPC 的 Socket,它的用途在這個函數中能體現出來,這個函數監聽客戶端的請求

/**
 * Runs the zygote process's select loop. Accepts new connections as
 * they happen, and reads commands from connections one spawn-request's
 * worth at a time.
 */
Runnable runSelectLoop(String abiList) {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

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

    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);
        }
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if ((pollFds[i].revents & POLLIN) == 0) {
                continue;
            }

            if (i == 0) {
                // 新客戶端連接請求,充當服務端 Socket
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                try {
                    ZygoteConnection connection = peers.get(i);
                    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) {
                   ...
                }
            }
        }
    }
}
  • 處理客戶端連接和客戶請求,其中客戶在 zygote 中用 ZygoteConnection 對象來表示
  • 客戶的請求在函數 processOneCommand 中處理

3. 總結

zygote 是 Android系統中創建 Java 世界的盤古,它創建第一個 Java 虛擬機,同時它又是女媧,繁殖了 framework 的核心 system_server 進程

  1. 第一天,創建 AppRuntime 對象,並調用他的 start,此後的活動由 AppRuntime 控制
  2. 第二天,調用 startVm 創建 Java 虛擬機,然後調用 startReg 來註冊 JNI 函數
  3. 第三天,通過 JNI 調用 com.android.internal.os.ZygoteInit 類的 main 函數,進入 Java 世界,但這個世界剛開始啥也沒有
  4. 第四天,調用 registerZygoteSocket,通過這個函數,它可以響應子孫後代的請求,同時 zygote 調用 preloadClasses 和 preloadResources,爲Java世界添磚加瓦
  5. 第五天,zygote 覺得自己的工作壓力大了,便通過調用 startSystemServer 分裂一個子進程 system_server 來爲 java 世界服務
  6. 第六天,zygote 調用 runSelectLoop – 等待並處理來自客戶的消息,後便睡去

以後的日子裏,zygote 隨時守護在我們周圍,當接收子孫後代的請求後,它會隨時醒來爲它們工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章