基於Android 7.0源碼,深入解析init進程及main函數的邏輯功能 ...
======================================================== ========================================================
= 【原創文章】:參考部分博客內容,學習之餘進行了大量的篩減細化分析 = = 【特殊申明】:避諱抄襲侵權之嫌疑,特此說明,歡迎轉載! =
======================================================== ========================================================
【前言】
Android啟動篇 — init原理(一)中講解分init進程分析init創建系統目錄並掛在相應系統文件、初始化屬性域、設置系統屬性、啟動配置屬性服務端等一系列複雜工作,很多工作和知識點跟Linux關係很大,所以沒有作過多介紹,而本此對於init.rc的解析則是重中之重,所以單獨拿出來進行詳細分析。
int main(int argc, char** argv) { /* 01. 創建文件系統目錄並掛載相關的文件系統 */ /* 02. 屏蔽標準的輸入輸出/初始化內核log系統 */ /* 03. 初始化屬性域 */ /* 04. 完成SELinux相關工作 */• /* 05. 重新設置屬性 */ /* 06. 創建epoll句柄 */ /* 07. 裝載子進程信號處理器 */ /* 08. 設置預設系統屬性 */ /* 09. 啟動配置屬性的服務端 */ /* 10. 匹配命令和函數之間的對應關係 */
------------------------------------------------------------------------------------------- // Android啟動篇 — init原理(一)中講解
/* 11. 解析init.rc */ Parser& parser = Parser::GetInstance(); // 構造解析文件用的parser對象 // 增加ServiceParser為一個section,對應name為service parser.AddSectionParser("service",std::make_unique<ServiceParser>()); // 增加ActionParser為一個section,對應name為action parser.AddSectionParser("on", std::make_unique<ActionParser>()); // 增加ImportParser為一個section,對應name為service parser.AddSectionParser("import", std::make_unique<ImportParser>()); parser.ParseConfig("/init.rc"); // 開始實際的解析過程
【正文】
init.rc是一個配置文件,內部由Android初始化語言編寫(Android Init Language)編寫的腳本,主要包含五種類型語句:Action、Command、Service、Option和Import,在分析代碼的過程中我們會詳細介紹。
init.rc的配置代碼在:system/core/rootdir/init.rc 中
init.rc文件是在init進程啟動後執行的啟動腳本,文件中記錄著init進程需執行的操作。
init.rc文件大致分為兩大部分,一部分是以“on”關鍵字開頭的動作列表(action list):
on early-init // Action類型語句 # Set init and its forked children's oom_adj. // #:註釋符號 write /proc/1/oom_score_adj -1000 ... ... start ueventd
Action類型語句格式:
on <trigger> [&& <trigger>]* // 設置觸發器 <command> <command> // 動作觸發之後要執行的命令
另一部分是以“service”關鍵字開頭的服務列表(service list): 如 Zygote
service ueventd /sbin/ueventd class core critical seclabel u:r:ueventd:s0
Service類型語句格式:
service <name> <pathname> [ <argument> ]* // <service的名字><執行程式路徑><傳遞參數> <option> // option是service的修飾詞,影響什麼時候、如何啟動services <option> ...
藉助系統環境變數或Linux命令,動作列表用於創建所需目錄,以及為某些特定文件指定許可權,而服務列表用來記錄init進程需要啟動的一些子進程。如上面代碼所示,service關鍵字後的第一個字元串表示服務(子進程)的名稱,第二個字元串表示服務的執行路徑。
值得一提的是在Android 7.0中對init.rc文件進行了拆分,每個服務一個rc文件。我們要分析的zygote服務的啟動腳本則在init.zygoteXX.rc中定義。
在init.rc的import段我們看到如下代碼:
import /init.${ro.zygote}.rc // 可以看出init.rc不再直接引入一個固定的文件,而是根據屬性ro.zygote的內容來引入不同的文件
說明:
從android5.0開始,android開始支持64位的編譯,zygote本身也就有了32位和64位的區別,所以在這裡用ro.zygote屬性來控制啟動不同版本的zygote進程。
init.rc位於/system/core/rootdir下。在這個路徑下還包括四個關於zygote的rc文件。分別是Init.zygote32.rc,Init.zygote32_64.rc,Init.zygote64.rc,Init.zygote64_32.rc,由硬體決定調用哪個文件。
這裡拿32位處理器為例,init.zygote32.rc的代碼如下所示:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main # class是一個option,指定zygote服務的類型為main socket zygote stream 660 root system # socket關鍵字表示一個option,創建一個名為dev/socket/zygote,類型為stream,許可權為660的socket onrestart write /sys/android_power/request_state wake # onrestart是一個option,說明在zygote重啟時需要執行的command onrestart write /sys/power/state on onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart netd writepid /dev/cpuset/foreground/tasks
“service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server”
在Init.zygote32.rc中,定義了一個zygote服務:zygote,由關鍵字service告訴init進程創建一個名為zygote的進程,這個進程要執行的程式是:/system/bin/app_process,給這個進程四個參數:
· -Xzygote:該參數將作為虛擬機啟動時所需的參數
· /system/bin:代表虛擬機程式所在目錄
· --zygote:指明以ZygoteInit.java類中的main函數作為虛擬機執行入口
· --start-system-server:告訴Zygote進程啟動SystemServer進程
接下來,我們回到源碼當中,繼續分析main函數:
/* 11. 解析init.rc */ Parser& parser = Parser::GetInstance(); // 構造解析文件用的parser對象 // 增加ServiceParser為一個section,對應name為service parser.AddSectionParser("service",std::make_unique<ServiceParser>()); // 增加ActionParser為一個section,對應name為action parser.AddSectionParser("on", std::make_unique<ActionParser>()); // 增加ImportParser為一個section,對應name為service parser.AddSectionParser("import", std::make_unique<ImportParser>()); parser.ParseConfig("/init.rc"); // 開始實際的解析過程
說明:
上面在解析init.rc文件時使用了Parser類(在init目錄下的init_parser.h中定義), 初始化ServiceParser用來解析 “service”塊,ActionParser用來解析"on"塊,ImportParser用來解析“import”塊,“import”是用來引入一個init配置文件,來擴展當前配置的。
/system/core/init/readme.txt 中對init文件中的所有關鍵字做了介紹,主要包含了Actions, Commands, Services, Options, and Imports等,可自行學習解讀。
分析init.rc的解析過程:函數定義於system/core/init/ init_parser.cpp中
bool Parser::ParseConfig(const std::string& path) { if (is_dir(path.c_str())) { // 判斷傳入參數是否為目錄地址 return ParseConfigDir(path); // 遞歸目錄,最終還是靠ParseConfigFile來解析實際的文件 } return ParseConfigFile(path); // 傳入傳輸為文件地址 }
繼續分析ParseConfigFile():
bool Parser::ParseConfigFile(const std::string& path) { ... ... Timer t; std::string data; if (!read_file(path.c_str(), &data)) { // 讀取路徑指定文件中的內容,保存為字元串形式 return false; } ... ... ParseData(path, data); // 解析獲取的字元串 ... ... }
跟蹤ParseData():
void Parser::ParseData(const std::string& filename, const std::string& data) { ... ... parse_state state; ... ... std::vector<std::string> args; for (;;) { switch (next_token(&state)) { // next_token以行為單位分割參數傳遞過來的字元串,最先走到T_TEXT分支 case T_EOF: if (section_parser) { section_parser->EndSection(); // 解析結束 } return; case T_NEWLINE: state.line++; if (args.empty()) { break; } // 在前文創建parser時,我們為service,on,import定義了對應的parser // 這裡就是根據第一個參數,判斷是否有對應的parser if (section_parsers_.count(args[0])) { if (section_parser) { // 結束上一個parser的工作,將構造出的對象加入到對應的service_list與action_list中 section_parser->EndSection(); } // 獲取參數對應的parser section_parser = section_parsers_[args[0]].get(); std::string ret_err; // 調用實際parser的ParseSection函數 if (!section_parser->ParseSection(args, &ret_err)) { parse_error(&state, "%s\n", ret_err.c_str()); section_parser = nullptr; } } else if (section_parser) { std::string ret_err; // 如果第一個參數不是service,on,import // 則調用前一個parser的ParseLineSection函數 // 這裡相當於解析一個參數塊的子項 if (!section_parser->ParseLineSection(args, state.filename, state.line, &ret_err)) { parse_error(&state, "%s\n", ret_err.c_str()); } } args.clear(); // 清空本次解析的數據 break; case T_TEXT: args.emplace_back(state.text); //將本次解析的內容寫入到args中 break; } } }
至此,init.rc解析完,接下來init會執行幾個重要的階段:
int main(int argc, char** argv) {
/* 01. 創建文件系統目錄並掛載相關的文件系統 */
/* 02. 屏蔽標準的輸入輸出/初始化內核log系統 */
/* 03. 初始化屬性域 */
/* 04. 完成SELinux相關工作 */•
/* 05. 重新設置屬性 */
/* 06. 創建epoll句柄 */
/* 07. 裝載子進程信號處理器 */
/* 08. 設置預設系統屬性 */
/* 09. 啟動配置屬性的服務端 */
/* 10. 匹配命令和函數之間的對應關係 */
/* 11. 解析init.rc*/
----------------------------------------------------------------------------
/* 12. 向執行隊列中添加其他action */
// 獲取ActionManager對象,需要通過am對命令執行順序進行控制 ActionManager& am = ActionManager::GetInstance(); // init執行命令觸發器主要分為early-init,init,late-init,boot等 am.QueueEventTrigger("early-init"); // 添加觸發器early-init,執行on early-init內容 // 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(keychord_init_action, "keychord_init"); am.QueueBuiltinAction(console_init_action, "console_init"); // Trigger all the boot actions to get us started. am.QueueEventTrigger("init"); // 添加觸發器init,執行on init內容,主要包括創建/掛在一些目錄,以及symlink等 // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random // wasn't ready immediately after wait_for_coldboot_done am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); // Don't mount filesystems or start core system services in charger mode. if (bootmode == "charger") { am.QueueEventTrigger("charger"); // on charger階段 } else if (strncmp(bootmode.c_str(), "ffbm", 4) == 0) { NOTICE("Booting into ffbm mode\n"); am.QueueEventTrigger("ffbm"); } else { am.QueueEventTrigger("late-init"); // 非充電模式添加觸發器last-init } // Run all property triggers based on current state of the properties. am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
在last-init最後階段有如下代碼:
# Mount filesystems and start core system services. on late-init trigger early-fs # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter # '--early' can be specified to skip entries with 'latemount'. # /system and /vendor must be mounted by the end of the fs stage, # while /data is optional. trigger fs trigger post-fs # Load properties from /system/ + /factory after fs mount. Place # this in another action so that the load will be scheduled after the prior # issued fs triggers have completed. trigger load_system_props_action # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter # to only mount entries with 'latemount'. This is needed if '--early' is # specified in the previous mount_all command on the fs stage. # With /system mounted and properties form /system + /factory available, # some services can be started. trigger late-fs # Now we can mount /data. File encryption requires keymaster to decrypt # /data, which in turn can only be loaded when system properties are present. trigger post-fs-data # Load persist properties and override properties (if enabled) from /data. trigger load_persist_props_action # Remove a file to wake up anything waiting for firmware. trigger firmware_mounts_complete trigger early-boot trigger boot
可見出發了on early-boot和on boot兩個Action。
我們看一下on boot:
on boot # basic network init ifup lo hostname localhost domainname localdomain ... ... class_start core
在on boot
的最後class_start core
會啟動class為core的服務,這些服務包括ueventd、logd、healthd、adbd(disabled)、lmkd(LowMemoryKiller)、servicemanager、vold、debuggerd、surfaceflinger、bootanim(disabled)等。
回到主題,分析trigger觸發器的代碼,QueueEventTrigger():位於system/core/init/action.cpp
void ActionManager::QueueEventTrigger(const std::string& trigger) { trigger_queue_.push(std::make_unique<EventTrigger>(trigger)); }
此處QueueEventTrigger函數就是利用參數構造EventTrigger,然後加入到trigger_queue_中。後續init進程處理trigger事件時,將會觸發相應的操作。
再看一下QueueBuiltinAction()函數:同樣位於system/core/init/action.cpp
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) { // 創建action auto action = std::make_unique<Action>(true); std::vector<std::string> name_vector{name}; // 保證唯一性 if (!action->InitSingleTrigger(name)) { return; } // 創建action的cmd,指定執行函數和參數 action->AddCommand(func, name_vector); trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get())); actions_.emplace_back(std::move(action)); }
QueueBuiltinAction函數中構造新的action加入到actions_中,第一個參數作為新建action攜帶cmd的執行函數;第二個參數既作為action的trigger name,也作為action攜帶cmd的參數。
接下來繼續分析main函數:
int main(int argc, char** argv) {
/* 01. 創建文件系統目錄並掛載相關的文件系統 */ /* 02. 屏蔽標準的輸入輸出/初始化內核log系統 */ /* 03. 初始化屬性域 */ /* 04. 完成SELinux相關工作 */• /* 05. 重新設置屬性 */ /* 06. 創建epoll句柄 */ /* 07. 裝載子進程信號處理器 */ /* 08. 設置預設系統屬性 */ /* 09. 啟動配置屬性的服務端 */ /* 10. 匹配命令和函數之間的對應關係 */
/* 11. 解析init.rc*/
/* 12. 向執行隊列中添加其他action */
-------------------------------------------------------------------
/* 13. 處理添加到運行隊列的事件 */
while (true) {
// 判斷是否有事件需要處理 if (!waiting_for_exec) { // 依次執行每個action中攜帶command對應的執行函數 am.ExecuteOneCommand(); // 重啟一些掛掉的進程 restart_processes(); } // 以下決定timeout的時間,將影響while迴圈的間隔 int timeout = -1; // 有進程需要重啟時,等待該進程重啟 if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } // 有action待處理,不等待 if (am.HasMoreCommands()) { timeout = 0; } // bootchart_sample應該是進行性能數據採樣 bootchart_sample(&timeout); epoll_event ev; // 沒有事件到來的話,最多阻塞timeout時間 int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout)); if (nr == -1) { ERROR("epoll_wait failed: %s\n", strerror(errno)); } else if (nr == 1) { //有事件到來,執行對應處理函數 //根據上文知道,epoll句柄(即epoll_fd)主要監聽子進程結束,及其它進程設置系統屬性的請求 ((void (*)()) ev.data.ptr)(); } } return 0; } // end main
看一下ExecuteOneComand()函數:同樣位於system/core/init/action.cpp
void ActionManager::ExecuteOneCommand() { // Loop through the trigger queue until we have an action to execute // 當前的可執行action隊列為空, trigger_queue_隊列不為空 while (current_executing_actions_.empty() && !trigger_queue_.empty()) { // 迴圈遍歷action_隊列,包含了所有需要執行的命令,解析init.rc獲得 for (const auto& action : actions_) { // 獲取隊頭的trigger, 檢查actions_列表中的action的trigger,對比是否相同 if (trigger_queue_.front()->CheckTriggers(*action)) { // 將所有具有同一trigger的action加入當前可執行action隊列 current_executing_actions_.emplace(action.get()); } } // 將隊頭trigger出棧 trigger_queue_.pop(); } if (current_executing_actions_.empty()) { // 當前可執行的actions隊列為空就返回 return; } auto action = current_executing_actions_.front(); // 獲取當前可執行actions隊列的首個action if (current_command_ == 0) { std::string trigger_name = action->BuildTriggersString(); INFO("processing action (%s)\n", trigger_name.c_str()); } action->ExecuteOneCommand(current_command_); // 執行當前的命令 // If this was the last command in the current action, then remove // the action from the executing list. // If this action was oneshot, then also remove it from actions_. ++current_command_; // 不斷疊加,將action_中的所有命令取出 if (current_command_ == action->NumCommands()) { current_executing_actions_.pop(); current_command_ = 0; if (action->oneshot()) { auto eraser = [&action] (std::unique_ptr<Action>& a) { return a.get() == action; }; actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser)); } } }
我們來觀察一下init.rc的開頭部分:
import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc import /init.usb.configfs.rc import /init.${ro.zygote}.rc // 後面我們即將重點分析zygote進程
通過ro.zygote的屬性import對應的zygote的rc文件。
我們查看init.zygote64_32.rc:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote class main 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 writepid /dev/cpuset/foreground/tasks service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary class main socket zygote_secondary stream 660 root system onrestart restart zygote writepid /dev/cpuset/foreground/tasks
可以看到zygote的class是main, 它是在on nonencrypted
時被啟動的,如下:
on boot # basic network init ifup lo hostname localhost domainname localdomain ... ... class_start core on nonencrypted # A/B update verifier that marks a successful boot. exec - root cache -- /system/bin/update_verifier nonencrypted class_start main class_start late_start
至此,Init.cpp的main函數分析完畢!init進程已經啟動完成,一些重要的服務如core服務和main服務也都啟動起來,並啟動了zygote(/system/bin/app_process64)進程,zygote初始化時會創建虛擬機,啟動systemserver等。