從源碼角度看Android系統Zygote進程啓動過程

在Android系統中,DVM、ART、應用程序進程和SystemServer進程都是由Zygote進程創建的,因此Zygote又稱爲“孵化器”。它是通過fork的形式來創建應用程序進程和SystemServer進程,由於Zygote進程在啓動時會創建一個DVM或者ART,因此通過fork而創建的應用程序進程和SystemServer進程可以在內部獲取一個DVM或ART的實例副本。

備註:本文將結合Android8.0的源碼看Zygote進程的啓動過程以及Zygote進程做了哪些重要工作。

1. Zygote進程啓動腳本

在init.rc中採用的是import類型語句來引入Zygote啓動腳本,這些啓動腳本都是由Android初始化語言來寫的:

import /init.${ro.zygote}.rc

可以看出:init.rc中不會直接引入一個固定的文件,而是根據屬性ro.zygote的內容來引入不同的文件。

從Android5.0開始,Android開始支持64位程序,Zygote就有了32位和64位的區別。所以這裏就用ro.zygote屬性來控制使用不同的Zygote啓動腳本,從而就啓動不同版本的Zygote進程。

ro.zygote屬性的值有如下四種:

  • init.zygote32.rc

  • init.zygote32_64.rc

  • init.zygote64.rc

  • init.zygote64_32.rc

這些Zygote啓動腳本都是放在system/core/rootdir/目錄下。

1.1 init.zygote32.rc

表示支持純32位程序,內容如下所示:

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

根據Service類型語句的格式可知:

  • Zygote進程名稱爲zygote

  • 執行程序是app_process

  • classname爲main

  • 如果audioserver、cameraserver、media、netd、wificond等進程終止時,Zygote進程會重啓。

1.2 init.zygote32_64.rc

表示既支持32位程序也支持64位程序,內容如下所示:

service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
    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

service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
    class main
    priority -20
    user root
    group root readproc
    socket zygote_secondary stream 660 root system
    onrestart restart zygote
    writepid /dev/cpuset/foreground/tasks

可以發現,腳本中有兩個Service類型語句,說明會啓動兩個Zygote進程:

  • 一個進程名爲:zygote;執行程序爲:app_process32,作爲主模式

  • 另一個進程名爲:zygote_secondary;執行程序爲:app_process64,作爲輔模式

2. Zygote進程啓動過程

《從源碼角度看Android系統init進程啓動過程》一文中可知:init啓動Zygote調用的是app_main.cpp的main函數中AppRuntime的start方法來啓動Zygote進程。

代碼路徑:frameworks/base/cmds/app_process/app_main.cpp

main函數如下:

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

	//傳到的參數argv是“-Xzygote /system/bin --zygote --start-system-server”
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    //忽略第一個參數 
    argc--;
    argv++;

    ...省略...

    // 參數解析
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

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

    Vector<String8> args;
    if (!className.isEmpty()) {
		// 運行application或tool程序
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);

        ...省略...

    } else {
        //進入zygote模式,創建 /data/dalvik-cache路徑
        maybeCreateDalvikCache();

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

        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);
        for (; i < argc; ++i) {
            args.add(String8(argv[i]));
        }
    }

	//設置進程名
    if (!niceName.isEmpty()) {
        runtime.setArgv0(niceName.string(), true /* setProcName */);
    }

    if (zygote) { //註釋3
        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處判斷參數arg中是否包含了“–zygote”,如果包含了則說明main函數是運行在Zygote進程中。將參數zygote設置爲true;並給niceName賦值,64位系統nice_name爲zygote64、32位系統nice_name爲zygote

  • 註釋2處判斷參數arg中是否包含了“–start-system-server”,如果包含了則說明main函數是運行在SystemServer進程中。將參數startSystemServer設置爲true。

  • 註釋3出判斷zygote是否爲 true,如果爲true,就說明運行在Zygote進程中,然後就調用AppRuntime中的start函數

深入到AppRuntime中的start函數中:

代碼路徑:frameworks/base/core/jni/AndroidRuntime.cpp

start函數如下:

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

    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
	//啓動java虛擬機
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    //爲java虛擬機註冊JNI方法
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    ...省略...
	//從app_main的main函數中可以傳過來的className是com.android.internal.os.ZygoteInit
    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);
    }

    //將com.android.internal.os.ZygoteInit轉換爲com/android/internal/os/ZygoteInit
    char* slashClassName = toSlashClassName(className);
	//找到ZygoteInit
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
		//找到ZygoteInit的main方法
        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 {
			//通過JNI調用ZygoteInit的main方法
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);

    ...省略...
}

從上面的註釋可以看出,start函數做了如下工作:

  • 調用startVm函數來創建Java虛擬機

  • 調用startReg函數爲Java虛擬機註冊JNI方法

  • 將傳進來的參數com.android.internal.os.ZygoteInit轉換爲com/android/internal/os/ZygoteInit,並賦值給slashClassName

  • 通過slashClassName找到ZygoteInit

  • 通過JNI調用ZygoteInit的main方法

在通過JNI調用ZygoteInit的main方法後,Zygote就進入了Java框架層。

深入到ZygoteInit方法中:

代碼路徑:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

main函數如下:

public static void main(String argv[]) {
    ZygoteServer zygoteServer = new ZygoteServer();

    ...省略...

    try {
        ...省略...
		//創建一個Server端的Socket,socketName的值爲“zygote”
        zygoteServer.registerServerSocket(socketName); //註釋1
        // 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());
			//預加載類和資源
            preload(bootTimingsTraceLog); //註釋2
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                SystemClock.uptimeMillis());
            bootTimingsTraceLog.traceEnd(); // ZygotePreload
        } else {
            Zygote.resetNicePriority();
        }

        ...省略...

		//啓動SystemServer進程
        if (startSystemServer) {
            startSystemServer(abiList, socketName, zygoteServer); //註釋3
        }
        Log.i(TAG, "Accepting command socket connections");
		//等待AMS請求
        zygoteServer.runSelectLoop(abiList); //註釋4
        zygoteServer.closeServerSocket();
    } catch (Zygote.MethodAndArgsCaller caller) {
        caller.run();
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        zygoteServer.closeServerSocket();
        throw ex;
    }
}

註釋解析:

  • 註釋1處通過registerServerSocket方法來創建一個Server端的Socket,這個name爲zygote的Socket用於等待ActivityManagerService請求Zygote創建新的應用程序進程

  • 註釋2預加載類和資源

  • 註釋3啓動SystemServer進程

  • 註釋4處調用ZygoteServer的runSelectLoop方法來等待AMS請求創建新的應用程序進程

綜上所述:ZygoteInit的main方法主要做了4件事:

  1. 創建一個Server端的Socket

  2. 預加載類和資源

  3. 啓動SystemServer進程

  4. 等待AMS請求創建新的應用程序進程

下面分別對這四件事跟蹤源碼分析下。

2.1 registerServerSocket

代碼路徑:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

深入到registerServerSocket函數中:

void registerServerSocket(String socketName) {
    if (mServerSocket == null) {
        int fileDesc;
		//拼接Socket的名稱
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; //註釋1
        try {
			//得到Socket的環境變量的值
            String env = System.getenv(fullSocketName); //註釋2
			//將Socket環境變量的值轉換爲文件描述符的參數
            fileDesc = Integer.parseInt(env); //註釋3
        } catch (RuntimeException ex) {
            throw new RuntimeException(fullSocketName + " unset or invalid", ex);
        }

        try {
			//創建文件描述符
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);
			//創建服務器端的Socket
            mServerSocket = new LocalServerSocket(fd); //註釋4
        } catch (IOException ex) {
            throw new RuntimeException(
                    "Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}

註釋解析:

  • 註釋1拼接Socket的名稱,其中ANDROID_SOCKET_PREFIX的值爲“ANDROID_SOCKET_”,socketName的值是傳進來的“zygote”,因此fullSocketName的值爲“ANDROID_SOCKET_zygote”

  • 註釋2處將fullSocketName轉換爲環境變量的值

  • 註釋3將環境變量的值轉換爲文件描述符參數

  • 註釋4創建LocalServerSocket,也就是服務器端的Socket

在Zygote進程將SystemServer進程啓動後,就會在這個服務器端的Socket上等待AMS請求Zygote進程來創建新的應用程序進程。

2.2 preload

代碼路徑:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

深入到preload函數中:

static void preload(BootTimingsTraceLog bootTimingsTraceLog) {
    Log.d(TAG, "begin preload");
    bootTimingsTraceLog.traceBegin("BeginIcuCachePinning");
    beginIcuCachePinning();
    bootTimingsTraceLog.traceEnd(); // BeginIcuCachePinning
    bootTimingsTraceLog.traceBegin("PreloadClasses");
	//預加載位於/system/etc/preloaded-classes文件中的類
    preloadClasses();

    bootTimingsTraceLog.traceEnd(); // PreloadClasses
    bootTimingsTraceLog.traceBegin("PreloadResources");
	預加載資源,包含drawable和color資源
    preloadResources();

    bootTimingsTraceLog.traceEnd(); // PreloadResources
    Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
	//預加載OpenGL
    preloadOpenGL();

    Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
	//通過System.loadLibrary()方法,
	//預加載"android","compiler_rt","jnigraphics"這3個共享庫
    preloadSharedLibraries();

	//預加載 文本連接符資源
    preloadTextResources();
    
	//僅用於zygote進程,用於內存共享的進程
    WebViewFactory.prepareWebViewInZygote();
    endIcuCachePinning();
    warmUpJcaProviders();
    Log.d(TAG, "end preload");

    sPreloadComplete = true;
}
  • 類加載,採用的是反射機制Class.forName()方法來加載。

  • 加載的資源主要是com.android.internal.R.array.preloaded_drawables和com.android.internal.R.array.preloaded_color_state_lists,在應用程序中以com.android.internal.R.xxx開頭的資源,就是在此時由Zygote加載到內存中的。

zygote進程內加載了preload()方法中的所有資源,當需要fork新進程時,採用copy on write技術,如下圖所示:

從上圖可以看出在創建新進程時,會共享父進程Zygote地址空間。

2.3 startSystemServer

代碼路徑:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

深入到startSystemServer函數中:

private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer)
        throws Zygote.MethodAndArgsCaller, RuntimeException {

    ...省略...

	//參數準備,args數組中保存啓動SystemServer的啓動參數
    String args[] = { //註釋1
        "--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); //註釋2
        ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
        ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

        //fork子進程,也是創建SystemServer進程
        pid = Zygote.forkSystemServer( //註釋3
                parsedArgs.uid, parsedArgs.gid,
                parsedArgs.gids,
                parsedArgs.debugFlags,
                null,
                parsedArgs.permittedCapabilities,
                parsedArgs.effectiveCapabilities);
    } catch (IllegalArgumentException ex) {
        throw new RuntimeException(ex);
    }

    //運行在子進程中
    if (pid == 0) {
        if (hasSecondZygote(abiList)) {
            waitForSecondaryZygote(socketName);
        }
		//關閉zygote原有的socket
        zygoteServer.closeServerSocket();
		//處理SystemServer進程
        handleSystemServerProcess(parsedArgs); //註釋4
    }
    return true;
}

註釋解析:

  • 註釋1創建args數組,主要用來保存啓動SystemServer的啓動參數。

其中:

可以看出SystemServer進程的的用戶id和用戶組id都被設置爲了1000,並且擁有用戶組1001-1010、1018、1021、1032、3001-3010的權限。

進程名爲:system_server

啓動的類名爲:com.android.server.SystemServer

  • 註釋2處將args數組封裝成Arguments對象,並給註釋3出的forkSystemServer函數調用

  • 註釋3處調用Zygote的forkSystemServer方法,其內部會調用nativeForkSystemServer這個Native方法,nativeForkSystemServer會通過fork函數在當前進程創建一個子進程,即SystemServer進程。

  • 註釋4處理SystemServer進程

2.4 runSelectLoop

在啓動SystemServer進程後,會執行ZygoteServer的runSelectLoop方法。

代碼路徑:frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

深入到runSelectLoop函數中:

void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
	//sServerSocket是socket通信中的服務端,即zygote進程。保存到fds[0]
    fds.add(mServerSocket.getFileDescriptor()); //註釋1
    peers.add(null);
	//無限循環等待AMS的請求
    while (true) {
        StructPollfd[] pollFds = new StructPollfd[fds.size()];
        for (int i = 0; i < pollFds.length; ++i) { //註釋2
            pollFds[i] = new StructPollfd();
            pollFds[i].fd = fds.get(i);
            pollFds[i].events = (short) POLLIN;
        }
        try {
			//處理輪詢狀態,當pollFds有事件到來則往下執行,否則阻塞在這裏
            Os.poll(pollFds, -1);
        } catch (ErrnoException ex) {
            throw new RuntimeException("poll failed", ex);
        }
        for (int i = pollFds.length - 1; i >= 0; --i) { //註釋3
			//採用I/O多路複用機制,當接收到客戶端發出連接請求或者數據處理請求時,則往下執行;
			//否則進入continue,跳出本次循環
            if ((pollFds[i].revents & POLLIN) == 0) {
                continue;
            }
            if (i == 0) {
				//即fds[0],代表的是sServerSocket,則意味着有客戶端連接請求;
				//則創建ZygoteConnection對象,並添加到fds
                ZygoteConnection newPeer = acceptCommandPeer(abiList); //註釋4
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                boolean done = peers.get(i).runOnce(this); //註釋5
                if (done) {
                    peers.remove(i);
                    fds.remove(i);
                }
            }
        }
    }
}

註釋解析:

  • 註釋1:處的mServerSocket就是在registerZygoteSocket函數中創建的服務端的Socket,調用getFileDescriptor()方法獲取該Socket的fd字段的值,並添加到fd列表fds中。

  • 註釋2處通過遍歷將fds存儲的信息轉移到pollFds中

  • 註釋3處對pollFds進行遍歷,如果i==0,說明服務端Socket與客戶端連接上了,即:當前Zygote進程與AMS建立了連接

  • 註釋4處通過acceptCommandPeer方法得到ZygoteConnection類並添加到Socket連接列表peers中,然後將ZygoteConnection的fd添加到fd列表fds中,以便接收到AMS發送過來的請求

  • 如果i不等於0,則說明AMS向Zygote進程發送了一個創建應用進程的請求,然後調用註釋5處ZygoteConnection的runOnce函數來創建一個新的應用程序進程,並在成功創建後,將這個連接從Socket連接列表peers和fds中移除。

3. 總結

3.1 Zygote啓動過程調用流程圖
3.2 Zygote進程啓動總結

Zygote進程啓動主要做了如下工作:

  • 解析init.${ro.zygote}.rc中的參數,創建AppRuntime並調用它的start方法

  • 調用AndroidRuntime的startVM()方法創建虛擬機,再調用startReg()註冊JNI函數

  • 通過JNI調用ZygoteInit的main函數,進入Zygote的Java框架層

  • 通過registerZygoteSocket方法建立Socket通道,zygote作爲通信的服務端,用於響應客戶端請求

  • preload()預加載通用類、drawable和color資源、openGL以及共享庫以及WebView,用於提高app啓動效率

  • 啓動SystemServer進程

  • 調用runSelectLoop(),隨時待命,當接收到請求創建新進程時,立即喚醒Zygote並執行相應工作

非常感謝您的耐心閱讀,希望我的文章對您有幫助。歡迎點評、轉發或分享給您的朋友或技術羣。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章