從Android 8.0源碼的角度剖析Android系統啓動過程(1)

 在《嵌入式Linux應用開發完全手冊》一書中,我們可以較爲清晰地瞭解到Android系統啓動的大體流程:當用戶按下Android設備的電源鍵時,CPU上電後會從0x00000000地址開始執行引導程序BootLoader。BootLoader是Android系統在上電後執行的第一段代碼,它被固化在ROM中,當系統上電後就會被加載到RAM中執行,該段代碼的目的是初始化硬件設備、準備好軟件環境,然後將Android系統鏡像裝載到RAM中,最後拉起Linux內核(通過調用start_kernel函數)。Linux內核被拉起後,首先會完成設置緩存、加載驅動等系統設置,然後會在系統文件中尋找init.rc文件,並啓動init進程,進程編號pid=1。該進程是Linux內核啓動的第一個用戶級進程,是所有其他進程的父進程,引導用戶空間服務,至此係統的運行由內核空間切換到用戶空間。init進程在完成初始化和啓動屬性服務後,會啓動Zygote進程(“孵化器”),該進程將完成Dalvik/ART虛擬機實例的創建和啓動SystemServer進程等操作。SystemServer進程會啓動各種系統服務,包括PMS、AMS、WMS等等,其中AMS服務被啓動後就會去啓動Launcher應用(桌面啓動器)。至此,Android系統啓動流程執行完畢。

1. init進程啓動過程

 init進程啓動時序圖如下:
在這裏插入圖片描述
 前面說到,Linux內核在加載完成後,它會在系統文件中尋找init.rc文件,並啓動init進程。init進程的入口函數位於Android源碼目錄system/core/init.cpp源文件中,函數名稱爲"main",它的源碼如下:

// 源碼路徑:system/core/init.cpp
int main(int argc, char** argv) {
   ...

    if (is_first_stage) {
        boot_clock::time_point start_time = boot_clock::now();

        // Clear the umask.
        umask(0);
        // 1. 創建和掛載啓動所需的文件目錄
        // Get the basic filesystem setup we need put together in the initramdisk
        // on / and then we'll let the rc file figure out the rest.
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        // Don't expose the raw commandline to unprivileged processes.
        chmod("/proc/cmdline", 0440);
        gid_t groups[] = { AID_READPROC };
        setgroups(arraysize(groups), groups);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
        mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
        mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));

        // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
        // talk to the outside world...
        // 初始化內核的LOG系統,便於從外界獲取Kernel的日誌
        InitKernelLogging(argv);

        LOG(INFO) << "init first stage started!";

        if (!DoFirstStageMount()) {
            LOG(ERROR) << "Failed to mount required partitions early ...";
            panic();
        }
        ...
    }

    // 2. 初始化屬性服務
    property_init();

    ...
    // 創建epoll句柄
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        PLOG(ERROR) << "epoll_create1 failed";
        exit(1);
    }
    // 3. 設置子進程異常捕獲處理函數 
    signal_handler_init();
    // 導入默認的環境變量
    property_load_boot_defaults();
    export_oem_lock_status();
    // 2. 啓動屬性服務
    start_property_service();
    set_usb_controller();

    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);
    // 4. 獲取一個Parser解析器對象
    Parser& parser = Parser::GetInstance();
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    parser.AddSectionParser("import", std::make_unique<ImportParser>());
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        // 4. 解析/init.rc文件
        parser.ParseConfig("/init.rc");
        parser.set_is_system_etc_init_loaded(
                parser.ParseConfig("/system/etc/init"));
        parser.set_is_vendor_etc_init_loaded(
                parser.ParseConfig("/vendor/etc/init"));
        parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
    } else {
        parser.ParseConfig(bootscript);
        parser.set_is_system_etc_init_loaded(true);
        parser.set_is_vendor_etc_init_loaded(true);
        parser.set_is_odm_etc_init_loaded(true);
    }

    ...
    ActionManager& am = ActionManager::GetInstance();
    ...

    while (true) {
        // By default, sleep until something happens.
        int epoll_timeout_ms = -1;
        // 5. 遍歷執行每個action中攜帶的command對應的執行函數
        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
            // 重啓死去的進程
            restart_processes();

            // If there's a process that needs restarting, wake up in time for that.
            if (process_needs_restart_at != 0) {
                epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
                if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
            }

            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout_ms = 0;
        }
		...
    }

    return 0;

}

 從上述源碼可知,init進程的入口函數main主要完成以下工作:

  • 創建和掛載啓動所需的文件目錄(1)。其中掛載了五種文件系統,包含tmpfsdevptsprocsysfsselinuxfs,它們都是系統運行時目錄,只有系統運行時纔會存在 ,當系統停止後會被刪除。
  • 初始化、啓動屬性服務(2)。類似於Windows的註冊表(使用鍵值對的形式記錄用戶和軟件的一些信息),屬性服務存儲一些屬性值,當系統或軟件需要這些屬性時便於直接讀取。init進程啓動時會爲這些屬性分配內存,並將其存儲到屬性服務中。
    // 初始化屬性服務
    property_init();
    // 啓動屬性服務
    start_property_service();
  • 設置子進程異常捕獲處理函數(3)。signal_handler_init函數用於設置子進程信號處理函數,主要是防止init進程的子進程變成殭屍進程。所謂殭屍進程,是指父進程通過fork創建子進程,在子進程終止後,但是父進程並不知道子進程已經終止了,就會導致系統進程表中爲子進程保存的進程號、運行時間等信息無法被刪除。由於系統進程表資源是有限的,隨着這種殭屍進程數量的增多,系統進程表將會被消耗殆盡,最終導致系統無法創建新的進程。因此,爲了防止殭屍進程,系統在子進程暫停或終止時發出SIGGHLD信號,而函數signal_handler_init接收到SIGGHLD信號後做進一步處理,以移除終止子進程的信息。
 	// 設置子進程信號處理函數
	// 接收SIGCHLD信號
	signal_handler_init();
  • 解析init.rc文件,循環執行
	// 解析init.rc文件
	Parser& parser = Parser::GetInstance();
	parser.ParseConfig("/init.rc");
	// 循環執行action中的Command
	ActionManager& am = ActionManager::GetInstance();
	while(true) {
       am.ExecuteOneCommand();
    }

 接下來,我們就詳細分析init.rc文件和init進程是如何啓動Zygoye進程的。

1.1 解析init.rc文件

 init.rc文件是Android系統啓動過程中必不可少的配置文件,它由AIL語言(Android Init Language,Android初始化語言)編寫,該語言主要包含五種類型語句:Action(行動)Command(命令)Service(服務)Option(選項)Import。AIL語言編寫的文件以.rc爲後綴,所有類型語句以爲單位,各種記號由空格隔開,註釋以井號(#)開頭,行末的反斜槓(\)用於換行。

  • Actions(行動)

 Actions實際上就是一個Commands(命令)序列。每個Actions有唯一的名字,都有一個trigger(觸發器),該trigger被用於決定action的執行時間。當一個符號action觸發條件的事件發生時,action會被加入到執行隊列的末尾。當然,隊列中的每一個action都會被依次提出,被提出的action中的每個command都將被依次執行。Actions的形式如下所示:

on <trigger>  # 設置觸發器,比如boot爲init進程啓動後第一個被觸發的Triggers
   <command>  # 動作觸發之後要執行的Command
   <command>
   ...
  • Command(命令)
# 創建和執行一個程序,<path>程序路徑 
 exec <path> [ <argument> ]*
 # 修改文件權限
 chmod <octal-mode> <path>
 # 創建目錄
 mkdir <path> [mode] [owner] [group]
 # 複製操作
 copy <path> <path>
 ...
  • Services(服務)

 Services是一個程序,它在初始化時啓動,並在退出時重啓(可選)。

# service <service名字> <執行程序路徑> [傳遞參數(可選,多個)]
service <name> <pathname> [<argument>]*
	<option>  # option是service的修飾詞,影響什麼時候,如何啓動service
	<option>
	...
  • Options(選項)

 Options是Services的修正者,它們影響Service在何時,並以何種方式運行。

# 指定一個服務類,所有同一類的服務可以同時啓動和停止
# 如果不通過class選項指定一個類,則默認爲"default"類服務
class <name>
# 在啓動這個服務前改變該服務的組名
group <groupname> [ <groupname> ]*
# 當服務重啓,執行一個命令
onrestart <command>
# 在啓動這個服務前改變該服務的用戶名
# 默認爲root
user <username>
# 創建一個Uinx域的名爲/dev/socket/<name>的套接字
# 並傳遞它的文件描述符給已啓動的進程
socket <name> <type> <perm> [ <user> [ <group> ] ]
  • Imports(引入)

 Imports用來引入一個要解析的其他配置文件,通常用於當前配置文件的擴展。如果path是一個目錄,則該目錄下的每個.rc文件都會被引入。

import <path>

 有了AIL語言基礎後,我們再來看init.rc配置文件,該文件位於Android源碼目錄/system/core/rootdir。

# 導入其他.rc文件
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
# init.zygote32.rc
# init.zygote64.rc
# init.zygote32_64.rc
import /init.${ro.zygote}.rc

on init
    sysclktz 0

    copy /proc/cmdline /dev/urandom
    copy /default.prop /dev/urandom
    ...
on boot
    # basic network init
    ifup lo
    hostname localhost
    domainname localdomain
 	...
# 啓動所有名爲classname爲main的Service    
on nonencrypted
    class_start main
    class_start late_start
...    
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
    class core
    critical
    seclabel u:r:ueventd:s0

service healthd /system/bin/healthd
    class core
    critical
    group root system wakelock

service console /system/bin/sh
    class core
    console
    disabled
    user shell
    group shell log readproc
    seclabel u:r:shell:s0
...

 init.rc配置文件內容很多,但是基本可以看出它在Android系統啓動過程中所起的作用,即根據不同的觸發條件完成各種操作,比如創建目錄、目錄文件拷貝、掛載等等,以及使用service語句通知init進程創建指定的進程,這些進程均是init進程的子進程,比如Zygote進程。需要注意的是,在Android8.0源碼中,init.rc文件中並沒有直接寫明創建zygote進程的語句,而是將相關的service語句寫到其他.rc文件中(每個service語句對應一個.rc文件),然後使用import語句導入。這裏以init.zygote64.rc爲例:

service zygote /system/bin/app_process64 -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語句的作用是:通知init進程創建一個名爲zygote的進程,這個進程執行程序的路徑爲/system/bin/app_process64,傳給程序app_process64的參數爲-Xzygote /system/bin --zygote --start-system-server。class main指的是Zygote的classname爲main;onrestart …表示當服務重啓時,執行執行的命令;等等。

1.2 啓動Zygote進程

 在1.1小節到談到,init進程是通過解析init.rc配置文件,來執行各種初始化、創建啓動進程等操作,其中就包括zygote進程。在init.rc有這麼一個Actions,即on nonencrypted,它有一個Command爲class_start main,這條語句的目的就是啓動那些classname爲main的Services,又由上面的分析可知,Zygote進程的classname就是main,因此,class_start這個Commands就是創建Zygote進程的起始點,它對應於函數do_class_start,被定義在system/core/init/builtins.cpp中。do_class_start函數源碼如下:

// system/core/init/builtins.cpp
static int do_class_start(const std::vector<std::string>& args) {
    // 遍歷Service鏈,依次執行StartIfNotDisabled()
    ServiceManager::GetInstance().
        ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
    return 0;
}

 在do_class_start函數中,它通過調用Services管理類ServiceManager的ForEachServiceInClass函數,去遍歷保存了一系列Service對象的鏈表,找到classname爲main的Zygote,並執行與之對應的Service對象的StartIfNotDisabled函數,被定義在system/core/init/service.cpp中。StartIfNotDisabled函數源碼如下:

// system/core/init/service.cpp
bool Service::StartIfNotDisabled() {
    // Service沒有在其對應的rc文件中設置disable選項
    if (!(flags_ & SVC_DISABLED)) {
        return Start();
    } else {
        flags_ |= SVC_DISABLED_START;
    }
    return true;
}

 StartIfNotDisabled函數實現很簡單,它會根據Service是否有在其對應的rc文件中設置disable選項,如果沒有,則執行Serivce對象的Start函數來啓動該Service。通過上面對init.zygote64.rc配置文件的分析,在Service語句中並沒有設置disable選項,因此SVC_DISABLED爲false。Start函數源碼如下:

// system/core/init/service.cpp
bool Service::Start() {
    // 如果Service已經運行,則不啓動
    if (flags_ & SVC_RUNNING) {
        return false;
    }
    ...
    // 判斷需要啓動的Service的對應的執行文件是否存在
    // 如果不存在,則不啓動Service。比如,這個Serivce名爲zygote
    // 那麼該Service對應的執行程序爲/system/bin/app_process64
    int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
    if (console_fd < 0) {
        flags_ |= SVC_DISABLED;
        return false;
    }
    close(console_fd);
    ...
    // init進程調用fork函數創建子進程
    pid_t pid = -1;
    if (namespace_flags_) {
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
        pid = fork();
    }
    ...
    // 當pid=0時
	// 說明當前代碼邏輯執行在子進程中
    if (pid == 0) {
        umask(077);
        
        ...
        // 啓動Service子進程,並進入該Service可執行文件的mian函數中
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
            PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
        }
    }
}

 在Service對象的Start函數中,首先,它會去判斷當前Service是否運行,如果正在運行則不再執行下面的流程;然後,檢查當前Service對應的可執行文件是否存在,如果不存在則不啓動該Service。關於Service對應的可執行文件,假如當前Service名爲"zygote",根據我們對分析init.zygote64.rc可知,名爲"zygote"的Service對應的執行程序爲/system/bin/app_process64,該執行程序對應的文件爲app_main.cpp再然後,init進程調用fork函數創建Service子進程;最後,根據pid=0時,當前代碼邏輯在子進程中運行,調用execve函數啓動被創建的Service子進程,並進入Service對應執行文件的main函數。假如當前Service名爲zygote,那麼隨着execve函數被調用,將進入app_main.cpp的main函數中執行。app_main.cpp的main函數源碼如下所示:

// Android8.0\frameworks\base\cmds\app_main.cpp
// argv[] = "-Xzygote /system/bin --zygote --start-system-server"
int main(int argc, char* const argv[])
{
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ...
        
    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;
    // 解析argv[]參數
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            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;
        }
    }
    // 調用AppRuntime的start函數
    // 啓動ZygoteInit
    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.");
    }
}

 從上述代碼可知,app_main.cpp的main函數首先會去循環解析由之前init.zgote64.rc中Service語句傳入進來的參數列表(-Xzygote /system/bin --zygote --start-system-server),根據參數信息將zygotestartSystemServer屬性置爲true,其中,startSystemServer後面會用到,這裏只用到當zygote爲true時,會去調用AppRuntime的start方法來啓動Zygote。也就是說,執行到這裏就進入了Zygote進程的啓動過程。

init進程啓動過程中做了很多工作,主要有如下四點:

  • 創建和掛載啓動所需的文件目錄;
  • 初始化和啓動屬性服務;
  • 設置子進程信號處理函數,防止子進程變成殭屍進程;
  • 解析init.rc配置文件並啓動Zygote進程;

2. Zygote進程啓動過程

 在常見內存泄漏與優化(二)一文中,我們曾談到所有的Android應用程序都是運行在獨立的Dalvik/ART虛擬機上的,而這個Dalvik或ART虛擬機就是由Zygote進程創建的。Zygote進程是Android中的"孵化器",在Android中,Dalvik/ART實例,所有的Android應用進程以及運行系統關鍵服務的SystemServer進程都是由Zygote進程創建的。Zygote進程在啓動時會創建Dalvik或ART的實例,在創建(“孵化”)其他進程時通過fork(複製)自身的方式實現。因此,通過Zygote進程fork自身創建的應用進程和SystemServer進程“天生”就擁有了Dalvik或ART的實例,這就避免了每個應用啓動在啓動時都要運行和初始化一個虛擬機所帶來的啓動/運行延遲。接下來,我們就接着1.2小節進一步分析Zygote進程的啓動過程,Zygote啓動過程是從AppRuntime$start函數開始的,由於AppRuntime繼承於AndroidRuntime(運行時類),因此,實際調用的時AndroidRuntime的start函數。start函數源碼如下:

// Android8.0\frameworks\base\core\jni\AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote){
	...
    JniInvocation jni_invocation;
    // 初始化,判斷是啓動Dalvik還是ART
    jni_invocation.Init(NULL);
    JNIEnv* env;
	// 1. 啓動虛擬機
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    // 爲虛擬機註冊JNI方法
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    ...
    // 將“com.android.internal.os.ZygoteInit”
    // 轉爲"com/android/internal/os/ZygoteInit"
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
    	// 2. 找到ZygoteInit的main方法,且ZygoteInit是一個Java類
    	// 位於Java層
        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 {
        	// 3. 通過JNI調用ZygoteInit的main方法
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
    }
    free(slashClassName);
}

 AndroidRuntime的main函數主要完成兩件事:(1) 創建虛擬機,併爲虛擬機註冊JNI方法;(2) 將“com.android.internal.os.ZygoteInit”替換爲"com/android/internal/os/ZygoteInit",然後通過JNI方法找到ZygoteInit的main方法,並調用該方法。由於ZygoteInit類由Java語言編寫,且位於Framework層(Java層),這也意味着Android系統的啓動流程由Native層進入Framework層。ZygoteInit的main方法源碼如下:

// Android8.0\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java
public static void main(String argv[]) {
    ZygoteServer zygoteServer = new ZygoteServer();
    ...
    try{
        boolean startSystemServer = false;
        String socketName = "zygote";
        String abiList = null;
        boolean enableLazyPreload = false;
        // 解析傳入的參數
        // 這裏可以得到startSystemServer=true
        for (int i = 1; i < argv.length; i++) {
            if ("start-system-server".equals(argv[i])) {
                startSystemServer = true;
            } else if ("--enable-lazy-preload".equals(argv[i])) {
                enableLazyPreload = true;
            } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                abiList = argv[i].substring(ABI_LIST_ARG.length());
            } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                socketName = argv[i].substring(SOCKET_NAME_ARG.length());
            } else {
                throw new RuntimeException("Unknown command line argument: );
            }
        }
		...
        // 1. 創建一個Server端的Socket
        // 這個Socket的名稱爲"zygote"
        zygoteServer.registerServerSocket(socketName);
        // 2. startSystemServer=true,啓動SystemServer進程
        if (startSystemServer) {
            startSystemServer(abiList, socketName, zygoteServer);
        }

        Log.i(TAG, "Accepting command socket connections");
        // 3. 循環等待client端的Socket請求服務
        //   這個client端指的是ActivityManagerService
        zygoteServer.runSelectLoop(abiList);
        zygoteServer.closeServerSocket();
    } catch (Zygote.MethodAndArgsCaller caller) {
        // 捕獲ygote.MethodAndArgsCaller異常
        caller.run();
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        zygoteServer.closeServerSocket();
        throw ex;
    }
}

 在ZygoteInit的main方法中,它首先會創建一個名爲"zygote"的Socket作爲服務端,這個Socket Server監聽等待作爲Socket client端的ActivityManagerService向其發送創建新的應用程序進程的請求;然後創建SystemServer進程,該進程主要用於啓動Android中各種關鍵的系統服務。下面,我們着重分析Zygote進程是如何創建SystemServer進程和創建一個Server Socket用於監聽AMS發送過來的請求。

2.1 啓動SystemServer進程

 在ZygoteInit的main方法中,通過調用ZygoteInit的startSystemServer方法發起創建SystemServer進程流程的,並向該方法傳入之前創建的Socket的名稱和Socket對象。startSystemServer方法源碼如下:

// Android8.0\frameworks\base\core\java\com\android\internal\os\ZygoteInit.java
private static boolean startSystemServer(String abiList, String socketName, 
                                         ZygoteServer zygoteServer)
    throws Zygote.MethodAndArgsCaller, RuntimeException {
    ...
        // SystemServer進程啓動參數
        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);

        // Zygote進程通過fork自身創建一個子進程
        // 即SystemServer進程
        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) {
        if (hasSecondZygote(abiList)) {
            waitForSecondaryZygote(socketName);
        }

        zygoteServer.closeServerSocket();
        // 處理SystemServer進程
        handleSystemServerProcess(parsedArgs);
    }

    return true;
}

 由上述源碼可知,Zygote進程創建SystemServer子進程是通過調用Zygote的forkSystemServer方法實現,該方法又調用了Zygote的nativeForkSystemServer方法,這個方法是一個native方法,最終由它在native層通過調用fork函數實現在當前進程(Zygote進程)創建一個子進程(SystemServer進程)。如果pid=0,說明SystemServer進程被創建成功,此時當前代碼邏輯運行在SystemServer子進程,接下來就會去調用Zygote的handleSystemServerProcess方法進入SystemServer進程的啓動過程。

2.2 創建Zygote進程的Server socket

 Zygote進程的Server Socket是通過調用ZygoteServer的registerServerSocket方法,該Server Socket的作用就是等待AMS請求Zygote進程來創建一個新的應用程序進程。registerServerSocket方法源碼如下:

// Android8.0\frameworks\base\core\java\com\android\internal\os\zygoteServer.java
void registerServerSocket(String socketName) {
    if (mServerSocket == null) {
        int fileDesc;
        // 拼接Socket的名稱
        // 即,ANDROID_SOCKET_PREFIX_zygote
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
        try {
            // 將Socket全稱轉換爲環境變量的值
            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);
            // 創建一個Server Socket
            // LocalServerSocket構造方法中爲創建Socket的流程
            mServerSocket = new LocalServerSocket(fd);
        } catch (IOException ex) {
            throw new RuntimeException(
                "Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}

 Zygote進程啓動SystemServer進程後(因爲SystemServer進程會去啓動各種系統關鍵服務,包括AMS,因此在等待請求之前需要提前創建它。),再去調用ZygoteServer的runSelectLoop方法無限等待AMS請求Zygote進程來創建一個新的應用程序進程。ZygoteServer的runSelectLoop方法源碼如下:

// Android8.0\frameworks\base\core\java\com\android\internal\os\ZygoteServer.java
    void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
		// 獲取Server Socket的fd字段的值
		// 添加到fds列表中
        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);
            }
		
            for (int i = pollFds.length - 1; i >= 0; --i) {
                if ((pollFds[i].revents & POLLIN) == 0) {
                    continue;
                }
				// AMS與Zygote進程建立連接
                if (i == 0) {
                    ZygoteConnection newPeer = acceptCommandPeer(abiList);
                    peers.add(newPeer);
                    fds.add(newPeer.getFileDesciptor());
                } else {
                    // 收到AMS發送的請求
					// 調用runOnce創建一個新的應用程序進程
                    boolean done = peers.get(i).runOnce(this);
                    if (done) {
                        peers.remove(i);
                        fds.remove(i);
                    }
                }
            }
        }
    }

 從runSelectLoop方法源碼可知,它首先會將存儲在Server Socket中的文件描述符fd取出,並插入到fds列表中;然後開啓循環等待,當i=0時,說明AMS與Zygote進程建立了Socket鏈接,並通過acceptCommandPeer方法獲取一個ZygoteConnection對象和將該對象的fd添加到fds列表中,以便可以接收到AMS發送過來的請求;最後,當i!=0時,說明AMS發送了一個新的創建應用程序進程的請求,此時Zygote進程就會去調用ZygoteConnection的runOnce方法進入新應用程序進程的創建工作。ZygoteConnection$runOnce方法源碼如下:

// Android8.0\frameworks\base\core\java\com\android\internal\os\ZygoteConnection.java
boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
    ...
        FileDescriptor fd = mSocket.getFileDescriptor();

    if (fd != null) {
        fdsToClose[0] = fd.getInt$();
    }

    fd = zygoteServer.getServerSocketFileDescriptor();

    if (fd != null) {
        fdsToClose[1] = fd.getInt$();
    }

    fd = null;
    // Zygote進程fork自身創建新的應用程序進程
    pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                                   parsedArgs.debugFlags, rlimits, 
                                   parsedArgs.mountExternal, parsedArgs.seInfo,
                                   parsedArgs.niceName, fdsToClose, fdsToIgnore, 
                                   parsedArgs.instructionSet,
                                   parsedArgs.appDataDir);
	...
	// pid=0
    // 當前代碼邏輯運行在子進程中
    try {
        if (pid == 0) {
            // in child
            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            // 處理子進程啓動邏輯
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

            // should never get here, the child is expected to either
            // throw Zygote.MethodAndArgsCaller or exec().
            return true;
        } else {
            // in parent...pid of < 0 means failure
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
        }
    } finally {
        IoUtils.closeQuietly(childPipeFd);
        IoUtils.closeQuietly(serverPipeFd);
    }
}

 runOnce方法主要完成兩個任務,一是調用Zygote的forkAndSpecialize方法創建一個子進程,該方法最終會調用Zygote的nativeForkAndSpecialize方法,這個方法是一個native方法,它將在native層調用fork函數實現子進程的創建;二是當pid=0時,說明子進程被創建成功且當前代碼運行在子進程中,接着就會去調用ZygoteConnection的handleChildProc方法進入子進程(新的應用程序進程)的啓動過程,這個我們下一節詳談。至此,Zygote進程的啓動過程大體分析完畢,它主要完成以下工作:

  • 創建Dalvik/ART虛擬機,爲虛擬機註冊JNI方法;
  • 通過JNI調用ZygoteInit的main方法進入Zygote的Java層;
  • 啓動SystemServer進程;
  • 創建Server Socket並等待AMS請求,然後創建新的應用程序進程。

Zygote進程啓動過程時序圖如下:
在這裏插入圖片描述
 最後,再解釋一下爲什麼在system\core\rootdir目錄下啓動Zygote進程的腳本有多個,這是因爲從Android5.0開始,Android開始支持64位程序,因此,Zygote也有了32位和64位之分,從之前分析的init.rc配置文件可知,Zygote進程的版本是通過ro.zygote屬性來控制使用對應的腳本文件的。如果是32位程序,則選擇腳本init.zygote32.rc來創建32位的Zygote進程;如果是64位程序,則選擇腳本init.zygote64.rc;如果32和64均支持,則選擇init.zygote32_64.rcinit.zygote64_32.rc腳本,執行其中的一個腳本都會創建兩個Zygote進程。以init.zygote32_64.rc爲例:

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

 該腳本會創建兩個Zygote進程,一個名爲zygote,執行程序爲/system/bin/app_process32,作爲主模式;另一個名爲zygote_secondary,執行程序爲/system/bin/app_process64,作爲輔模式。init.zygote64_32.rc腳本恰好與init.zygote32_64.rc功能相反。

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