天天看點

鳥人的Android揭秘(14)——Init程序源代碼分析(五)

      前一篇主要講解了init程序如何建立套接字以處理子程序終止,接下來我們繼續分析init程序啟動屬性服務,以及分析init.rc的過程。

      接下來init程序需要設定系統屬性,如代碼3-16所示。

property_load_boot_defaults();
export_oem_lock_status();
           

代碼 3-16 main()-設定系統屬性

      property_load_boot_defaults()函數實際上就是調用load_properties_from_file()[1]函數解析“/default.prop”配置檔案,然後根據解析的結果設定系統屬性。export_oem_lock_status()函數根據系統狀态設定“ro.boot.flash.locked”屬性。這部分代碼較為簡單,不深入分析。

      然後啟動屬性服務,它是init程序最主要的功能之一。init程序在共享記憶體區域中,建立并初始化屬性域。其它程序可以通路屬性域中的值,但更改屬性值僅能在init程序中進行。其它程序修改屬性值時,要預先向init程序送出屬性值變更申請,然後init程序處理該申請并修改屬性值。在通路和修改屬性時,init程序都可以進行權限控制。

代碼 3-17 main()-啟動屬性服務

      如前所述,屬性值的更改僅能在init程序中進行,為此init程序生成“/dev/socket/property_service”套接字,以接收其它程序送出的申請。

      接下來init程序開始解析init.rc檔案并執行相關的指令。init.rc檔案大緻分為兩大部分,一部分是以“on”關鍵字開頭的動作清單(Action List),另一部分是以“service”關鍵字開頭的服務清單(Service List)。動作清單用于建立所需目錄,以及為某些特定檔案指定權限,而服務清單用來記錄init程序需要啟動的一些子程序。此外,還有一個關鍵字“import”,用于導入另外一個.rc檔案,解析生成動作清單和服務清單,這些清單會分别被添加到init.rc檔案已生成的服務清單和動作清單中。

      代碼3-18所示建立了init.rc所需的解析器(Parser),用于解析“service”、“on”、“import”等關鍵字開始的清單。

const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
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>());
           

代碼 3-18 main()-建立init.rc解析器

      然後便開始解析init.rc檔案。init.rc檔案是在init程序啟動後執行的啟動腳本,檔案中記錄着init程序執行的功能。在Linux系統中,它被定義在根檔案系統的“/etc/rc.d/”目錄下,是啟動時的可執行檔案,在“/etc”目錄下儲存着設定環境變量的腳本。但在Android系統中,僅使用init.rc與init.{hardware}.rc兩個檔案,init.rc檔案在Android系統運作過程中用于通用的環境設定及程序相關的定義,init.{hardware}.rc用于定義Android在不同平台下的特定程序和環境設定等。

parser.ParseConfig("/init.rc");
           

代碼 3-19 main()-解析init.rc檔案

      ParseConfig()方法傳入參數是“/init.rc”,解析運作時與init程序同在根目錄下的init.rc[2]檔案。ParseConfig()方法實質上是調用ParseData()方法進行解析。執行該方法,讀取并分析init.rc檔案後,生成服務清單與動作清單。相對以前的代碼,在Android 7.0的代碼中,init.rc檔案的解析已經做了較好的封裝,動作清單解析後儲存在ActionManager[3]類的actions_向量中,類似的,服務清單儲存到ServiceManager[4]類的services_向量中。

      init程序會依次執行“early-init,init,late-init”片段中的指令,如代碼3-20所示,init程序利用參數構造EventTrigger,然後加入到trigger_queue_中,後續init程序處理trigger事件時,将會觸發相應的操作。init程序在後續代碼依次構造了“early-init”、“init”、“late-init”的事件觸發器,代碼僅以構造“early-init”觸發器為例。

am.QueueEventTrigger("early-init");
           

代碼 3-20 main()-向執行隊列中添加指令

      下面的代碼構造新的action加入到actions_中,第一個參數作為建立action攜帶cmd的執行函數;第二個參數既作為action的trigger name,也作為action攜帶cmd的參數。

// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
           

代碼 3-21 main()-構造新的Action

      init程序在緊接着還會構造幾個action,使用方法與上述代碼非常類似,此處不再贅述。下一篇我們将講解init程序如何處理上文構造出來的這些trigger、action和service。

[1] load_properties_from_file()函數定義在system/core/init/property_service.cpp。

[2] init.rc檔案在編譯前,定義在system/core/rootdir/init.rc中。

[3] ActionManager定義在system/core/init/action.h中。

[4] ServiceManager定義在system/core/init/service.h中。