天天看點

Android系統10 RK3399 init程序啟動(四十二) init.rc檔案解析邏輯

說明

系統:Android10.0

裝置: FireFly RK3399 (ROC-RK3399-PC-PLUS)

前言

Android init啟動的時候會解析init.rc, 當然還有很多其他rc檔案, 在init程序代碼中有對應的解析器, 本章節重點介紹init.rc中所有的内容是如何被解析的,了解這個解析邏輯, 有利于你對rc檔案中action和service實際的執行邏輯,友善後期根據需求做一些深度定制化。

一, 整體解析邏輯框圖

Android系統10 RK3399 init程式啟動(四十二) init.rc檔案解析邏輯

整個解析過程都是面向對象, 有解析器Parser, Action,Command, ActionManger, Service對象, Option對象, ServiceList對象。

二, 解析邏輯

Init在解析RC檔案的時候會有如下邏輯:

  1. 解析以section為機關, section包括import, action, service語句。
  2. 不同的section有不同的parser解析器,都繼承自SectionParser, 每個parser從上往下解析, 有分析開始(ParseSection), 每一行分析(ParseLineSection), 以及結束分析(EndSection/EndFile)。
  3. ImportParser分析import段落, 将所有的需要導入的檔案名拿到, 然後一個一個的分析裡面的其他語句。
  4. ActionParser分析action段落, 每個action段落會建構一個Action對象, 每個Action對象包含了所有的指令對象Command, 由ActionManager負責管理和排程所有Action對象。
  5. ServiceParsere分析service段落, 每個service段落會建構一個Service對象, 該對象會記錄服務對應的可執行檔案路徑和參數, 根據不同的option來初始化service對象不同的成員。 最終ServiceList進行管理和排程。

對應代碼在system/core/init/init.cpp中

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;
    // ServiceParser做的事情: 1,首先根據第一行的名字和參數建立出service對象,
    // 2,然後根據選項域的内容填充service對象,
    // 3.最後将建立出的service對象加入到vector類型的service連結清單(service_list_)中。
    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;
}

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("/product_services/etc/init")) {
            late_import_paths.emplace_back("/product_services/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);
    }
}      

代碼詳細過程,我利用思維導圖展開了: 

Android系統10 RK3399 init程式啟動(四十二) init.rc檔案解析邏輯

 所有的解析器都是繼承自SectionParser:

class SectionParser {
  public:
    virtual ~SectionParser() {}
    // 一個Section開始需要執行的, 如遇到import, on, service關鍵詞表示開始
    virtual Result<Success> ParseSection(std::vector<std::string>&& args,
                                         const std::string& filename, int line) = 0;
    // 每個section中的每一行, 除了第一行
    virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
    //這兩個都表示section解析結束之後需要做的事情, Endfile主要針對import
    virtual Result<Success> EndSection() { return Success(); };
    virtual void EndFile(){};
};      

大家感興趣可以去讀一下代碼, 讀代碼隻需要去閱讀各個SectionParser的以上三個接口,并且按照順序讀就能了解整個邏輯了。

三, 所有Action的執行的先後順序

啟動的過程中,會解析各個Action和Service, 其中Action的執行先後順序為如下所示:

on SetupCgroups

on early-init

on wait_for_coldboot_done

on MixHwrngIntoLinuxRng

on SetMmapRndBits

on SetKptrRestrict

on KeychordInit

on console_init

on init

on StartBoringSslSelfTest

on MixHwrngIntoLinuxRng

on InitBinder

on queue_property_triggers

on late-init

on early-fs

on fs

on post-fs

on late-fs

on post-fs-data

on zygote-start

on early-boot

四,總結

繼續閱讀