Android系統(tǒng)啟動流程(二) —— Zygote進(jìn)程的啟動流程

接上篇: Android系統(tǒng)啟動流程(一)init進(jìn)程的啟動流程

Zygote在英語中是受精卵的意思,從這個名字可以看出,zygote進(jìn)程是用來孵化其他進(jìn)程的,SystemServer和其他應(yīng)用程序進(jìn)程都是由Zygote進(jìn)程所創(chuàng)建的。Zygote是以服務(wù)的形式存在于Android系統(tǒng)中的,是Android系統(tǒng)的一個重要的守護(hù)進(jìn)程,下面我們通過源碼來分析Zygote進(jìn)程的啟動流程。

1.解析Zygote服務(wù)的啟動腳本并啟動app_main

在init進(jìn)程啟動時,會解析Zygote服務(wù)進(jìn)程的啟動腳本并開啟Zygote進(jìn)程,針對不同位數(shù)的操作系統(tǒng),Zygote也分別對應(yīng)不同的啟動腳本,在Android8.0系統(tǒng)的源碼中共有4個啟動腳本,分別是init.zygote32.rc(支持32位系統(tǒng))、init.zygote64.rc(支持64位系統(tǒng))、init.zygote32_64.rc(同時支持32位和64位,但以32位為主)、init.zygote64_32.rc(同時支持32位和64位,但以64位為主),我們以init.zygote32.rc為例來看一下Zygote服務(wù)的腳本源碼:
目錄位置:\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

從上面的代碼可以看出,該服務(wù)的名稱是zygote,路徑為/system/bin/app_process,參數(shù)包含-Xzygote /system/bin --zygote --start-system-server,class的名稱為main。

init進(jìn)程在解析完上面的代碼后,會對zygote服務(wù)進(jìn)行啟動,啟動部分的腳本代碼如下:
源碼位置:\system\core\rootdir\init.rc

on zygote-start && property:ro.crypto.state=unencrypted  //在.rc文件中,on表示一個觸發(fā)器,zygote-start是觸發(fā)器的名稱
  //當(dāng)該觸發(fā)器被觸發(fā)后,便會執(zhí)行下面的命令
    exec_start update_verifier_nonencrypted
    start netd
    start zygote  //啟動zygote服務(wù)
    start zygote_secondary

上面的代碼是定義在init.rc中的一個觸發(fā)器,當(dāng)該觸發(fā)器被觸發(fā)后,便會執(zhí)行start zygote這行命令,從而啟動zygote服務(wù),start命令對應(yīng)的函數(shù)為do_start,源碼如下:
源碼路徑:\system\core\init\builtins.cpp

static int do_start(const std::vector<std::string>& args) {
    // 1.根據(jù)service的名稱找到該服務(wù)
    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);  
    if (!svc) {
        LOG(ERROR) << "do_start: Service " << args[1] << " not found";
        return -1;
    }
    if (!svc->Start())  //2.調(diào)用Start方法開啟該服務(wù)
        return -1;
    return 0;
}

在注釋1處通過service的名稱來找到zygote這個服務(wù)的實(shí)例,然后再注釋2出調(diào)用Service的Start方法來開啟這個服務(wù),我們來看一下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函數(shù)創(chuàng)建zygote子進(jìn)程
    }

    if (pid == 0) {  //pid為0,說明當(dāng)前在子進(jìn)程中
        ...
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {  // 2.調(diào)用execve執(zhí)行子進(jìn)程的代碼
            PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
        }

        _exit(127);
    }

    ...
    return true;
}

在注釋1處通過fork函數(shù)創(chuàng)建了一個子線程,由于fork函數(shù)是對父進(jìn)程的自我復(fù)制,所以fork函數(shù)會同時在父進(jìn)程和子進(jìn)程中返回,并在父進(jìn)程中返回子進(jìn)程的id,在子進(jìn)程中返回0。

在注釋2處,通過調(diào)用execve函數(shù)來執(zhí)行子進(jìn)程的代碼,從zygote的啟動腳本中可以看到,該服務(wù)的執(zhí)行代碼位于 /system/bin/app_process中,對應(yīng)的文件為app_main.cpp,這樣程序即進(jìn)入了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.創(chuàng)建AppRuntime實(shí)例

    ...

    while (i < argc) {  // 2.循環(huán)遍歷參數(shù)
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {  //當(dāng)參數(shù)為“--zygote”時
            zygote = true;  //將zygote標(biāo)記變?yōu)閠rue
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) { //當(dāng)參數(shù)為“--start-system-server”時
            startSystemServer = true;  //將startSystemServer參數(shù)變?yōu)閠rue
        } 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標(biāo)志為true,則執(zhí)行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處創(chuàng)建了一個AppRuntime實(shí)例。

在注釋2處對參數(shù)進(jìn)行循環(huán)遍歷,如果參數(shù)中含有 "--zygote",則將zygote置為true,如果參數(shù)中含有"--start-system-server",則將startSystemServer置為true。

在注釋3處通過調(diào)用runtime的start方法來執(zhí)行ZygoteInit文件中的代碼,并將ZygoteInit的文件路徑作為參數(shù)傳入了start方法,這是一個java文件。start函數(shù)的源碼位于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虛擬機(jī)
        return;
    }
    onVmCreated(env);

     //主要用于注冊jni函數(shù)
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    ...
    /* 2.通過jni的方式執(zhí)行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的方式調(diào)用ZygoteInit的mian方法
        ...
    }
    ...
}

由于ZygoteInit文件是由java代碼編寫的,因此我們需要用jni的方法來執(zhí)行ZygoteInit的main方法。

在注釋1處,通過startVm方法創(chuàng)建了java虛擬機(jī)。

在注釋2處,通過執(zhí)行一系列的jni方法,最終調(diào)用了ZygoteInit的main方法。

3.啟動SystemServer并持續(xù)監(jiān)聽?wèi)?yīng)用創(chuàng)建請求。

我們來看一下Zygote的main方法的源碼:
源碼路徑:\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java

public static void main(String argv[]) {
        ZygoteServer zygoteServer = new ZygoteServer();  //創(chuàng)建ZygoteServer實(shí)例

        ...
       
        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.開啟事件循環(huán),不斷監(jiān)聽新的請求

            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方法中,首先創(chuàng)建了一個ZygoteServer實(shí)例,然后在注釋1處,通過調(diào)用zygoteServer的registerServerSocket方法對ServerSocket進(jìn)行了注冊。

在注釋2處調(diào)用startSystemServer方法開啟了SystemServer進(jìn)程。

在注釋3處通過調(diào)用zygoteServer的runSelectLoop方法開啟了事件循環(huán),這樣zygote進(jìn)程就可以持續(xù)監(jiān)聽新的應(yīng)用進(jìn)程創(chuàng)建請求。

我們先來看一下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名稱,最后的結(jié)果為“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();//創(chuàng)建文件描述符對象
                fd.setInt$(fileDesc);
                mServerSocket = new LocalServerSocket(fd);//創(chuàng)建ServerSocket對象
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex);
            }
        }
    }

在registerServerSocket方法中,首先通過字符串拼接的方式獲得了Socket的名稱,然后根據(jù)這個名稱創(chuàng)建了一個文件描述符對象。基于linux一切都是文件的思想,socket也被看作是一個文件,該文件描述符即用來表示該socket。

然后通過這個文件描述符創(chuàng)建了一個LocalServerSocket對象,通過名稱我們便可以看出,這是一個運(yùn)行于服務(wù)端的socket,它的作用便是用來監(jiān)聽新的應(yīng)用進(jìn)程創(chuàng)建請求。

我們再來看一下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.開始無限循環(huán),不斷監(jiān)聽新的請求 */
        while (true) {
            /* 將fds中的數(shù)據(jù)轉(zhuǎn)移到pollFds數(shù)組中 */
            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.當(dāng)數(shù)組中沒有未執(zhí)行的任務(wù)時
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);//不斷進(jìn)行監(jiān)聽,當(dāng)有新請求時便會返回

                    //將這個連接請求放入數(shù)組中
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } else {
                    boolean done = peers.get(i).runOnce(this);// 3.從peers中去除連接請求并執(zhí)行
                    if (done) {
                        //執(zhí)行完成后從數(shù)組中移除
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }

在runSelectLoop方法中,首先創(chuàng)建了一個FileDescriptor數(shù)組和一個ZygoteConnection數(shù)組,他們用來存儲新接收到的請求。在注釋1處開啟了一個無限循環(huán)來不斷監(jiān)聽新的請求,因此zygote進(jìn)程在android系統(tǒng)的運(yùn)行過程中會一直存在,直到系統(tǒng)關(guān)閉。

在這個無限循環(huán)中,先將fds數(shù)組的數(shù)據(jù)轉(zhuǎn)移到了pollFds數(shù)組中,然后對pollFds數(shù)組進(jìn)行了遍歷,當(dāng)i==0時,說明數(shù)組中所有請求任務(wù)都已經(jīng)執(zhí)行完了,那么調(diào)用acceptCommandPeer方法來獲取新的請求,acceptCommandPeer方法是一個阻塞方法,如果沒有新的連接請求,acceptCommandPeer會一直阻塞,直到有新的連接請求到來時,acceptCommandPeer才會將這個新的請求返回。獲取到新的請求后便將這個請求放入peers和fds數(shù)組中。

當(dāng)i不為0時,說明數(shù)組中還存在未執(zhí)行的請求,則將請求取出并調(diào)用runOnce方法來執(zhí)行這個請求。

我們先來看一下acceptCommandPeer方法的源碼:

private ZygoteConnection acceptCommandPeer(String abiList) {
        try {
            return createNewConnection(mServerSocket.accept(), abiList);//調(diào)用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);  //創(chuàng)建ZygoteConnection實(shí)例
    }

acceptCommandPeer的源碼非常簡單,就是就是調(diào)用accept方法等待新的請求,該方法會一直阻塞當(dāng)前線程,直到有新的請求到來。

我們再來看一下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方法的代碼很長,我們只看最關(guān)鍵的一句代碼,即調(diào)用forkAndSpecialize方法創(chuàng)建新的進(jìn)程,該方法最終會調(diào)用native方法來fork新的應(yīng)用程序進(jìn)程。

之前我們講過,zygote進(jìn)程在啟動的時候會創(chuàng)建一個java虛擬機(jī),而我們的應(yīng)用程序進(jìn)程都是由zygote進(jìn)程fork得來的,而fork的本質(zhì)是對父進(jìn)程的自我復(fù)制,因此所有的應(yīng)用程序子進(jìn)程也會獲得一個復(fù)制而來的java虛擬機(jī)副本,這樣便無需在應(yīng)用程序進(jìn)程中單獨(dú)啟動java虛擬機(jī)了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容