安卓啓動流程(一) - rc文件初步解析

init進程的一個核心部分,是通過解析rc文件,執行Action和啓動Service。在分析init進程前,有必要先學習rc文件的配置和解析的原理。

初始化rc文件解析相關工具

/system/core/init/init.cpp

int main(int argc, char** argv) {
    ...
    // 設置Action內建函數跳轉表
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);

    // 創建供應商子進程
    subcontexts = InitializeSubcontexts();

    // ActionManager實例, 維護rc文件解析的Action數據
    ActionManager& am = ActionManager::GetInstance();
    // ServiceList實例, 維護rc文件解析的Service數據
    ServiceList& sm = ServiceList::GetInstance();
  
    // 開始解析RC文件
    LoadBootScripts(am, sm);
    ...
}
  • BuiltinFunctionMap
    Action數據的內建函數映射表,維護了Action數據中,Command的命令函數映射關係和目標函數配置。

    例如 /system/core/logd/logd.rc 中:

    chmod 0644 /dev/event-log-tags
    

    Commond的命令是chmod,對應參數爲0644/dev/event-log-tags。在BuiltinFunctionMap中的映射配置爲:

    // chmod: Key, 命令名稱
    // {2, 2, {}}: Value, FunctionInfo, 命令映射的函數信息
    //     參數1: 最少參數量,即參數量下限
    //     參數2: 最多參數量,即參數量上限
    //     參數3: Function, 具體函數和執行環境
    // {true, do_chmod}:
    //     參數1: 是否需要在vendor_init域的子進程中運行,SELinux相關
    //     參數2: 具體需要調用的方法
    {"chmod", {2, 2, {true, do_chmod}}},
    
  • subcontexts
    init子進程集合。
    /system/core/init/subcontext.cpp

    const std::string kVendorContext = "u:r:vendor_init:s0";
    const char* const paths_and_secontexts[2][2] = {
          {"/vendor", kVendorContext.c_str()},
          {"/odm", kVendorContext.c_str()},
    };
    

    通過分析源碼,subcontexts對應2個子進程,SELinux context都爲"u:r:vendor_init:s0"
    目標函數將根據BuiltinFunctionMap的配置,運行在init進程或上述init子進程中。

    這個設計,主要解決供應商自己的初始化邏輯引入的安全風險,針對性的引入權限收窄的vendor_init域子進程,供給供應商運行自己的初始化邏輯。

    參考資料:
    供應商 init | Android 開源項目 | Android Open Source Project (google.cn)

  • ActionManager
    用於維護解析後的Action數據鏈表

  • ServiceList
    用於維護解析後的Service數據鏈表


解析RC文件

/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);
    }
}

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;
    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
    return parser;
}

通過CreateParser, 創建了Parser解析器對象,其解析規則如下:

匹配項 解析器 示例
service ServiceParser service ueventd /sbin/ueventd
on ActionParser on early-init
import ImportParser import /init.${ro.zygote}.rc

然後開始執行解析過程

  • 設備有定義ro.boot.init_rc屬性,直接加載屬性配置的rc文件。
  • 設備沒定義ro.boot.init_rc屬性,加載/init.rc文件。然後嘗試加載/system/etc/init/vendor/etc/init/odm/etc/init。加載失敗時,則把文件路徑加入late_import_paths隊列,延後加載。

最後調用Parser解析器的ParseConfig函數執行解析。


下一篇:安卓啓動流程(二) - Parser解析器

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