Android系統(tǒng)從init進程到Launcher(一)

前言

Android系統(tǒng)中最先被我們感知的就是Launcher界面,對于基于Android系統(tǒng)的智能設(shè)備來說,對Launcher進行定制是非常重要的一個環(huán)節(jié),那么了解Launcher的加載和啟動就顯得尤為重要,因此接下來會用幾章的篇幅進行分析闡述其中的原理。

Launcher啟動流程圖

對上述流程圖略作說明,當(dāng)按下啟動電源時,系統(tǒng)啟動后會加載引導(dǎo)程序,引導(dǎo)程序又會啟動 Linux 內(nèi)核,在加載 Linux 內(nèi)核時,它會在系統(tǒng)文件中尋找 init.rc 文件,并啟動 init 進程,之后會按著上圖中綠色部分依次啟動直至最后啟動 Launcher , 至此Android桌面展現(xiàn)。實際上Android系統(tǒng)的啟動流程要比上圖復(fù)雜的多,為了便于理解做了簡化,而接下來的幾篇會按著 init、Zygote、SystemService、ActivityManagerService、Launcher 這五個方面分別論述。

各進程間關(guān)系

由圖中可以看出,他們之間的通訊過程大致是:

  1. Launcher通過AIDL(Binder的一種)通知SystemService
  2. SystemService通過Socket通知Zygote
  3. Zygote 收到SystemService請求,fork自身生成應(yīng)用程序
  4. 應(yīng)用程序進程與SystemService通過Binder與AMS,WMS進行交互

Init 進程任務(wù)

  1. 創(chuàng)建和掛載啟動所需要的文件目錄
  2. 初始化和啟動屬性服務(wù)
  3. 解析 init.rc 配置文件并啟動 Zygote 進程

Init 源碼分析

為避免陷入到龐雜的源碼中,本小節(jié)會盡量的抽取 init 進程中的主要脈絡(luò)作為說明.
init 進程是 Android 啟動的第一個進程,進程號為 1 ,其源碼主要存在于 "/system/core/init" 目錄下。

(1) Init 進程入口

init 進程的入口函數(shù)為 main ,代碼如下所示:

int main(int argc, char** argv) {
    
    ...

    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);

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

        // Clear the umask.
        umask(0);

        clearenv();
        
        // 創(chuàng)建和掛載啟動所需的文件目錄 注釋 ①   
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        chmod("/proc/cmdline", 0440);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
        mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
        mkdir("/mnt/vendor", 0755);
        ...
        
        // 初始化 Kernel 的 Log,這樣就可以從外界獲取 Kernel 的日志  注釋②
        InitKernelLogging(argv);
        
    }
    ...
    // 初始化屬性服務(wù)配置 注釋 ③
    property_init();

    ...
    // 用于設(shè)置紫禁城信號處理函數(shù),如果子進程(Zygote進程)異常退出,init 進程會調(diào)用該函數(shù)中
    //設(shè)定的信號處理函數(shù)來進行處理  注釋 ④
    sigchld_handler_init();

  
    property_load_boot_defaults();
    export_oem_lock_status();
   // 啟動屬性服務(wù) 注釋 ⑤
    start_property_service();
    set_usb_controller();
    ...
    // 加載和解析 init.rc 文件 注釋⑥
    LoadBootScripts(am, sm);
  
    return 0;
}

(2) 屬性服務(wù)

Windows 平臺采用注冊表管理器來保存用戶、軟件的一些使用信息。這樣即使系統(tǒng)或者軟件重啟,就可以根據(jù)注冊表中的記錄,進行相應(yīng)的初始化工作。Android 提供類似的機制,叫做 屬性服務(wù) 。
在上述 main 函數(shù)中與屬性服務(wù)相關(guān)的代碼有兩行:

property_init();
start_property_service();

先來看看兩個函數(shù)的具體實現(xiàn):"/system/core/init/property_service.cpp"

void property_init() {
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
}

__system_property_area_init 函數(shù)用來初始化屬性內(nèi)存區(qū)域,接下來看看 start_property_service 函數(shù)的具體實現(xiàn):

void start_property_service() {
    selinux_callback cb;
    cb.func_audit = SelinuxAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    property_set("ro.property_service.version", "2");
    
    // 創(chuàng)建非阻塞的 Socket   注釋 ①
    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, nullptr);
    if (property_set_fd == -1) {
        PLOG(FATAL) << "start_property_service socket creation failed";
    }
    // 調(diào)用 listen 函數(shù)監(jiān)聽 property_set_fd 注釋 ②
    listen(property_set_fd, 8);
    // epoll 替換 select 處理大批量的文件描述符,handle_property_set_fd 用來處理客戶端的請求
    register_epoll_handler(property_set_fd, handle_property_set_fd);
}

可以看出在 注釋 ② 處調(diào)用 listen 函數(shù)對 property_set_fd 進行監(jiān)聽,這樣創(chuàng)建的 Socket 就成為 server,也就是 屬性服務(wù) ;listen 函數(shù)的第二個參數(shù)設(shè)置為 8 ,意味著屬性服務(wù)最多可以同時為8個試圖設(shè)置屬性的用戶提供服務(wù);

(3) 加載和解析 init.rc 文件

init 進程通過解析 init.rc 文件去啟動 Zygote 進程,這里通過 LoadBootScripts() 函數(shù)實現(xiàn):
/system/core/init/init.cpp:

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

如果沒有特殊配置ro.boot.init_rc,則解析./init.rc,把 /system/etc/init、/product/etc/init、/product_services/etc/init、/odm/etc/init、/vendor/etc/init 這幾個路徑加入init.rc之后解析的路徑,在init.rc解析完成后,解析這些目錄里的rc文件。這里需要說明一下,在 Android 7.0 之后,init.rc 進行了拆分,每個服務(wù)都有自己的 rc 文件,它們基本都被加載到這幾個目錄中,待解析之后,執(zhí)行相關(guān)的動作。關(guān)于 init.rc 文件的組成結(jié)構(gòu)可以參見 init.rc結(jié)構(gòu)說明

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

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

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