Android系統啓動流程(二) —— Zygote進程的啓動流程

接上篇: Android系統啓動流程(一)init進程的啓動流程java

Zygote在英語中是受精卵的意思,從這個名字能夠看出,zygote進程是用來孵化其餘進程的,SystemServer和其餘應用程序進程都是由Zygote進程所建立的。Zygote是以服務的形式存在於Android系統中的,是Android系統的一個重要的守護進程,下面咱們經過源碼來分析Zygote進程的啓動流程。linux

1.解析Zygote服務的啓動腳本並啓動app_main

在init進程啓動時,會解析Zygote服務進程的啓動腳本並開啓Zygote進程,針對不一樣位數的操做系統,Zygote也分別對應不一樣的啓動腳本,在Android8.0系統的源碼中共有4個啓動腳本,分別是init.zygote32.rc(支持32位系統)、init.zygote64.rc(支持64位系統)、init.zygote32_64.rc(同時支持32位和64位,但以32位爲主)、init.zygote64_32.rc(同時支持32位和64位,但以64位爲主),咱們以init.zygote32.rc爲例來看一下Zygote服務的腳本源碼:android

目錄位置:\system\core\rootdir\init.zygote32.rcios

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
複製代碼

從上面的代碼能夠看出,該服務的名稱是zygote,路徑爲/system/bin/app_process,參數包含-Xzygote /system/bin --zygote --start-system-server,class的名稱爲main。c++

init進程在解析完上面的代碼後,會對zygote服務進行啓動,啓動部分的腳本代碼以下:數組

源碼位置:\system\core\rootdir\init.rcmarkdown

on zygote-start && property:ro.crypto.state=unencrypted  //在.rc文件中,on表示一個觸發器,zygote-start是觸發器的名稱
  //當該觸發器被觸發後,便會執行下面的命令
    exec_start update_verifier_nonencrypted
    start netd
    start zygote  //啓動zygote服務
    start zygote_secondary
複製代碼

上面的代碼是定義在init.rc中的一個觸發器,當該觸發器被觸發後,便會執行start zygote這行命令,從而啓動zygote服務,start命令對應的函數爲do_start,源碼以下:app

源碼路徑:\system\core\init\builtins.cppsocket

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

在註釋1處經過service的名稱來找到zygote這個服務的實例,而後再註釋2出調用Service的Start方法來開啓這個服務,咱們來看一下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函數建立zygote子進程
    }

    if (pid == 0) {  //pid爲0,說明當前在子進程中
        ...
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {  // 2.調用execve執行子進程的代碼
            PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
        }

        _exit(127);
    }

    ...
    return true;
}
複製代碼

在註釋1處經過fork函數建立了一個子線程,因爲fork函數是對父進程的自我複製,因此fork函數會同時在父進程和子進程中返回,並在父進程中返回子進程的id,在子進程中返回0。

在註釋2處,經過調用execve函數來執行子進程的代碼,從zygote的啓動腳本中能夠看到,該服務的執行代碼位於 /system/bin/app_process中,對應的文件爲app_main.cpp,這樣程序即進入了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.建立AppRuntime實例

    ...

    while (i < argc) {  // 2.循環遍歷參數
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {  //當參數爲「--zygote」時
            zygote = true;  //將zygote標記變爲true
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) { //當參數爲「--start-system-server」時
            startSystemServer = true;  //將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;
        }
    }

    if (zygote) {
        // 3.若是zygote標誌爲true,則執行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處建立了一個AppRuntime實例。

在註釋2處對參數進行循環遍歷,若是參數中含有 "--zygote",則將zygote置爲true,若是參數中含有"--start-system-server",則將startSystemServer置爲true。

在註釋3處經過調用runtime的start方法來執行ZygoteInit文件中的代碼,並將ZygoteInit的文件路徑做爲參數傳入了start方法,這是一個java文件。start函數的源碼位於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虛擬機
        return;
    }
    onVmCreated(env);

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

    ...
    /* 2.經過jni的方式執行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的方式調用ZygoteInit的mian方法
        ...
    }
    ...
}
複製代碼

因爲ZygoteInit文件是由java代碼編寫的,所以咱們須要用jni的方法來執行ZygoteInit的main方法。

在註釋1處,經過startVm方法建立了java虛擬機。

在註釋2處,經過執行一系列的jni方法,最終調用了ZygoteInit的main方法。

3.啓動SystemServer並持續監聽應用建立請求。

咱們來看一下Zygote的main方法的源碼:

源碼路徑:\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java

public static void main(String argv[]) {
        ZygoteServer zygoteServer = new ZygoteServer();  //建立ZygoteServer實例

        ...
       
        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.開啓事件循環,不斷監聽新的請求

            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方法中,首先建立了一個ZygoteServer實例,而後在註釋1處,經過調用zygoteServer的registerServerSocket方法對ServerSocket進行了註冊。

在註釋2處調用startSystemServer方法開啓了SystemServer進程。

在註釋3處經過調用zygoteServer的runSelectLoop方法開啓了事件循環,這樣zygote進程就能夠持續監聽新的應用進程建立請求。

咱們先來看一下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名稱,最後的結果爲「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();//建立文件描述符對象
                fd.setInt$(fileDesc);
                mServerSocket = new LocalServerSocket(fd);//建立ServerSocket對象
            } catch (IOException ex) {
                throw new RuntimeException(
                        "Error binding to local socket '" + fileDesc + "'", ex);
            }
        }
    }
複製代碼

在registerServerSocket方法中,首先經過字符串拼接的方式得到了Socket的名稱,而後根據這個名稱建立了一個文件描述符對象。基於linux一切都是文件的思想,socket也被看做是一個文件,該文件描述符即用來表示該socket。

而後經過這個文件描述符建立了一個LocalServerSocket對象,經過名稱咱們即可以看出,這是一個運行於服務端的socket,它的做用即是用來監聽新的應用進程建立請求。

咱們再來看一下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.開始無限循環,不斷監聽新的請求 */
        while (true) {
            /* 將fds中的數據轉移到pollFds數組中 */
            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.當數組中沒有未執行的任務時
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);//不斷進行監聽,當有新請求時便會返回

                    //將這個鏈接請求放入數組中
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } else {
                    boolean done = peers.get(i).runOnce(this);// 3.從peers中取出鏈接請求並執行
                    if (done) {
                        //執行完成後從數組中移除
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }
複製代碼

在runSelectLoop方法中,首先建立了一個FileDescriptor數組和一個ZygoteConnection數組,他們用來存儲新接收到的請求。在註釋1處開啓了一個無限循環來不斷監聽新的請求,所以zygote進程在android系統的運行過程當中會一直存在,直到系統關閉。

在這個無限循環中,先將fds數組的數據轉移到了pollFds數組中,而後對pollFds數組進行了遍歷,當i==0時,說明數組中全部請求任務都已經執行完了,那麼調用acceptCommandPeer方法來獲取新的請求,acceptCommandPeer方法是一個阻塞方法,若是沒有新的鏈接請求,acceptCommandPeer會一直阻塞,直到有新的鏈接請求到來時,acceptCommandPeer纔會將這個新的請求返回。獲取到新的請求後便將這個請求放入peers和fds數組中。

當i不爲0時,說明數組中還存在未執行的請求,則將請求取出並調用runOnce方法來執行這個請求。

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

private ZygoteConnection acceptCommandPeer(String abiList) {
        try {
            return createNewConnection(mServerSocket.accept(), abiList);//調用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);  //建立ZygoteConnection實例
    }
複製代碼

acceptCommandPeer的源碼很是簡單,就是就是調用accept方法等待新的請求,該方法會一直阻塞當前線程,直到有新的請求到來。

咱們再來看一下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方法的代碼很長,咱們只看最關鍵的一句代碼,即調用forkAndSpecialize方法建立新的進程,該方法最終會調用native方法來fork新的應用程序進程。

以前咱們講過,zygote進程在啓動的時候會建立一個java虛擬機,而咱們的應用程序進程都是由zygote進程fork得來的,而fork的本質是對父進程的自我複製,所以全部的應用程序子進程也會得到一個複製而來的java虛擬機副本,這樣便無需在應用程序進程中單獨啓動java虛擬機了。

相關文章
相關標籤/搜索