Android啟動篇 — init原理(一)中講解分init進程分析init創建系統目錄并掛在相應系統文件、初始化屬性域、設置系統屬性、啟動配置屬性服務端等一系列復雜工作,很多工作和知識點跟Linux關系很大,所以沒有作過多介紹,而本此對于init.rc的解析則是重中之重,所以單獨拿出來進行詳細分析。

Java代碼
  1. int main(int argc, char** argv) {  
  2.     /* 01. 創建文件系統目錄并掛載相關的文件系統 */  
  3.     /* 02. 屏蔽標準的輸入輸出/初始化內核log系統 */  
  4.     /* 03. 初始化屬性域 */  
  5.     /* 04. 完成SELinux相關工作 */•  
  6.     /* 05. 重新設置屬性 */  
  7.     /* 06. 創建epoll句柄 */  
  8.     /* 07. 裝載子進程信號處理器 */  
  9.     /* 08. 設置默認系統屬性 */  
  10.     /* 09. 啟動配置屬性的服務端 */  
  11.     /* 10. 匹配命令和函數之間的對應關系 */  
  12. -------------------------------------------------------------------------------------------   // Android啟動篇 — init原理(一)中講解  
  13.     /* 11. 解析init.rc */  
  14.     Parser& parser = Parser::GetInstance();       // 構造解析文件用的parser對象  
  15.     // 增加ServiceParser為一個section,對應name為service  
  16.     parser.AddSectionParser("service",std::make_unique<ServiceParser>());  
  17.     // 增加ActionParser為一個section,對應name為action  
  18.     parser.AddSectionParser("on", std::make_unique<ActionParser>());  
  19.     // 增加ImportParser為一個section,對應name為service  
  20.     parser.AddSectionParser("import", std::make_unique<ImportParser>());  
  21.     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):

XML/HTML代碼
  1. on early-init      // Action類型語句  
  2.     # Set init and its forked children's oom_adj.     // #:注釋符號  
  3.     write /proc/1/oom_score_adj -1000  
  4.     ... ...  
  5.     start ueventd  

  Action類型語句格式:

XML/HTML代碼
  1. on <trigger> [&& <trigger>]*     // 設置觸發器    
  2.    <command>    
  3.    <command>      // 動作觸發之后要執行的命令  

  另一部分是以“service”關鍵字開頭的服務列表(service list):  如 Zygote

XML/HTML代碼
  1. service ueventd /sbin/ueventd  
  2.     class core  
  3.     critical  
  4.     seclabel u:r:ueventd:s0  

  Service類型語句格式:

XML/HTML代碼
  1. service <name> <pathname> [ <argument> ]*   // <service的名字><執行程序路徑><傳遞參數>    
  2.    <option>       // option是service的修飾詞,影響什么時候、如何啟動services    
  3.    <option>    
  4.    ...  

  借助系統環境變量或Linux命令,動作列表用于創建所需目錄,以及為某些特定文件指定權限,而服務列表用來記錄init進程需要啟動的一些子進程。如上面代碼所示,service關鍵字后的第一個字符串表示服務(子進程)的名稱,第二個字符串表示服務的執行路徑。

  值得一提的是在Android 7.0中對init.rc文件進行了拆分,每個服務一個rc文件。我們要分析的zygote服務的啟動腳本則在init.zygoteXX.rc中定義。

  在init.rc的import段我們看到如下代碼:

XML/HTML代碼
  1. 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的代碼如下所示:

XML/HTML代碼
  1. service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server  
  2.     class main         # class是一個option,指定zygote服務的類型為main  
  3.     socket zygote stream 660 root system          # socket關鍵字表示一個option,創建一個名為dev/socket/zygote,類型為stream,權限為660的socket  
  4.     onrestart write /sys/android_power/request_state wake          # onrestart是一個option,說明在zygote重啟時需要執行的command  
  5.     onrestart write /sys/power/state on  
  6.     onrestart restart audioserver  
  7.     onrestart restart cameraserver  
  8.     onrestart restart media  
  9.     onrestart restart netd  
  10.     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函數:

Java代碼
  1. /* 11. 解析init.rc */  
  2. Parser& parser = Parser::GetInstance();       // 構造解析文件用的parser對象  
  3. // 增加ServiceParser為一個section,對應name為service  
  4. parser.AddSectionParser("service",std::make_unique<ServiceParser>());  
  5. // 增加ActionParser為一個section,對應name為action  
  6. parser.AddSectionParser("on", std::make_unique<ActionParser>());  
  7. // 增加ImportParser為一個section,對應name為service  
  8. parser.AddSectionParser("import", std::make_unique<ImportParser>());  
  9. 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中

Java代碼
  1. bool Parser::ParseConfig(const std::string& path) {  
  2.     if (is_dir(path.c_str())) {           // 判斷傳入參數是否為目錄地址  
  3.         return ParseConfigDir(path);      // 遞歸目錄,最終還是靠ParseConfigFile來解析實際的文件  
  4.     }  
  5.     return ParseConfigFile(path);         // 傳入傳輸為文件地址  
  6. }  

  繼續分析ParseConfigFile():

Java代碼
  1. bool Parser::ParseConfigFile(const std::string& path) {  
  2.     ... ...  
  3.     Timer t;  
  4.     std::string data;  
  5.     if (!read_file(path.c_str(), &data)) {       // 讀取路徑指定文件中的內容,保存為字符串形式  
  6.         return false;  
  7. }  
  8. ... ...  
  9.     ParseData(path, data);        // 解析獲取的字符串  
  10.     ... ...  
  11. }  

  跟蹤ParseData():

Java代碼
  1. void Parser::ParseData(const std::string& filename, const std::string& data) {  
  2.     ... ...  
  3.     parse_state state;  
  4.     ... ...  
  5.     std::vector<std::string> args;  
  6.   
  7.     for (;;) {  
  8.         switch (next_token(&state)) {    // next_token以行為單位分割參數傳遞過來的字符串,最先走到T_TEXT分支  
  9.         case T_EOF:  
  10.             if (section_parser) {  
  11.                 section_parser->EndSection();    // 解析結束  
  12.             }  
  13.             return;  
  14.         case T_NEWLINE:  
  15.             state.line++;  
  16.             if (args.empty()) {  
  17.                 break;  
  18.             }  
  19.             // 在前文創建parser時,我們為service,on,import定義了對應的parser   
  20.             // 這里就是根據第一個參數,判斷是否有對應的parser  
  21.             if (section_parsers_.count(args[0])) {  
  22.                 if (section_parser) {  
  23.                     // 結束上一個parser的工作,將構造出的對象加入到對應的service_list與action_list中  
  24.                     section_parser->EndSection();  
  25.                 }  
  26.                 // 獲取參數對應的parser  
  27.                 section_parser = section_parsers_[args[0]].get();  
  28.                 std::string ret_err;  
  29.                 // 調用實際parser的ParseSection函數  
  30.                 if (!section_parser->ParseSection(args, &ret_err)) {  
  31.                     parse_error(&state, "%s\n", ret_err.c_str());  
  32.                     section_parser = nullptr;  
  33.                 }  
  34.             } else if (section_parser) {  
  35.                 std::string ret_err;  
  36.                 // 如果第一個參數不是service,on,import  
  37.                 // 則調用前一個parser的ParseLineSection函數  
  38.                 // 這里相當于解析一個參數塊的子項  
  39.                 if (!section_parser->ParseLineSection(args, state.filename,   
  40.                                                              state.line, &ret_err)) {  
  41.                     parse_error(&state, "%s\n", ret_err.c_str());  
  42.                 }  
  43.             }  
  44.             args.clear();       // 清空本次解析的數據  
  45.             break;  
  46.         case T_TEXT:  
  47.             args.emplace_back(state.text);     //將本次解析的內容寫入到args中  
  48.             break;  
  49.         }  
  50.     }  
  51. }  

  至此,init.rc解析完,接下來init會執行幾個重要的階段:

Java代碼
  1. int main(int argc, char** argv) {  
  2.     /* 01. 創建文件系統目錄并掛載相關的文件系統 */  
  3.     /* 02. 屏蔽標準的輸入輸出/初始化內核log系統 */  
  4.     /* 03. 初始化屬性域 */  
  5.     /* 04. 完成SELinux相關工作 */•  
  6.     /* 05. 重新設置屬性 */  
  7.     /* 06. 創建epoll句柄 */  
  8.     /* 07. 裝載子進程信號處理器 */  
  9.     /* 08. 設置默認系統屬性 */  
  10.     /* 09. 啟動配置屬性的服務端 */  
  11.     /* 10. 匹配命令和函數之間的對應關系 */  
  12.     /* 11. 解析init.rc*/  
  13. ----------------------------------------------------------------------------  
  14.   /* 12.  向執行隊列中添加其他action */  
  15.     // 獲取ActionManager對象,需要通過am對命令執行順序進行控制  
  16.     ActionManager& am = ActionManager::GetInstance();  
  17.     // init執行命令觸發器主要分為early-init,init,late-init,boot等  
  18.     am.QueueEventTrigger("early-init");    // 添加觸發器early-init,執行on early-init內容  
  19.   
  20.     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...  
  21.     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");  
  22.     // ... so that we can start queuing up actions that require stuff from /dev.  
  23.     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  24.     am.QueueBuiltinAction(keychord_init_action, "keychord_init");  
  25.     am.QueueBuiltinAction(console_init_action, "console_init");  
  26.   
  27.     // Trigger all the boot actions to get us started.  
  28.     am.QueueEventTrigger("init");        // 添加觸發器init,執行on init內容,主要包括創建/掛在一些目錄,以及symlink等  
  29.   
  30.     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random  
  31.     // wasn't ready immediately after wait_for_coldboot_done  
  32.     am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");  
  33.   
  34.     // Don't mount filesystems or start core system services in charger mode.  
  35.     if (bootmode == "charger") {  
  36.     am.QueueEventTrigger("charger");     // on charger階段  
  37.     } else if (strncmp(bootmode.c_str(), "ffbm"4) == 0) {  
  38.     NOTICE("Booting into ffbm mode\n");  
  39.     am.QueueEventTrigger("ffbm");  
  40.     } else {  
  41.     am.QueueEventTrigger("late-init");          // 非充電模式添加觸發器last-init  
  42.     }  
  43.   
  44.     // Run all property triggers based on current state of the properties.  
  45.     am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");  

  在last-init最后階段有如下代碼:

XML/HTML代碼
  1. # Mount filesystems and start core system services.  
  2. on late-init  
  3.     trigger early-fs  
  4.   
  5.     # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter  
  6.     # '--early' can be specified to skip entries with 'latemount'.  
  7.     # /system and /vendor must be mounted by the end of the fs stage,  
  8.     # while /data is optional.  
  9.     trigger fs  
  10.     trigger post-fs  
  11.   
  12.     # Load properties from /system/ + /factory after fs mount. Place  
  13.     # this in another action so that the load will be scheduled after the prior  
  14.     # issued fs triggers have completed.  
  15.     trigger load_system_props_action  
  16.   
  17.     # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter  
  18.     # to only mount entries with 'latemount'. This is needed if '--early' is  
  19.     # specified in the previous mount_all command on the fs stage.  
  20.     # With /system mounted and properties form /system + /factory available,  
  21.     # some services can be started.  
  22.     trigger late-fs  
  23.   
  24.     # Now we can mount /data. File encryption requires keymaster to decrypt  
  25.     # /data, which in turn can only be loaded when system properties are present.  
  26.     trigger post-fs-data  
  27.   
  28.     # Load persist properties and override properties (if enabled) from /data.  
  29.     trigger load_persist_props_action  
  30.   
  31.     # Remove a file to wake up anything waiting for firmware.  
  32.     trigger firmware_mounts_complete  
  33.   
  34.     trigger early-boot  
  35.    trigger boot  

  可見出發了on early-boot和on boot兩個Action。

  我們看一下on boot:

XML/HTML代碼
  1. on boot  
  2.     # basic network init  
  3.     ifup lo  
  4.     hostname localhost  
  5.     domainname localdomain  
  6.     ... ...  
  7.     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

Java代碼
  1. void ActionManager::QueueEventTrigger(const std::string& trigger) {  
  2.     trigger_queue_.push(std::make_unique<EventTrigger>(trigger));  
  3. }  

  此處QueueEventTrigger函數就是利用參數構造EventTrigger,然后加入到trigger_queue_中。后續init進程處理trigger事件時,將會觸發相應的操作。

  再看一下QueueBuiltinAction()函數:同樣位于system/core/init/action.cpp

Java代碼
  1. void ActionManager::QueueBuiltinAction(BuiltinFunction func,  
  2.                                    const std::string& name) {  
  3.     // 創建action  
  4.     auto action = std::make_unique<Action>(true);  
  5.     std::vector<std::string> name_vector{name};  
  6.   
  7.     // 保證唯一性  
  8.     if (!action->InitSingleTrigger(name)) {  
  9.         return;  
  10.     }  
  11.   
  12.     // 創建action的cmd,指定執行函數和參數  
  13.     action->AddCommand(func, name_vector);  
  14.   
  15.     trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));  
  16.     actions_.emplace_back(std::move(action));  
  17. }  

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

  接下來繼續分析main函數:

Java代碼
  1. int main(int argc, char** argv) {  
  2.     /* 01. 創建文件系統目錄并掛載相關的文件系統 */  
  3.     /* 02. 屏蔽標準的輸入輸出/初始化內核log系統 */  
  4.     /* 03. 初始化屬性域 */  
  5.     /* 04. 完成SELinux相關工作 */•  
  6.     /* 05. 重新設置屬性 */  
  7.     /* 06. 創建epoll句柄 */  
  8.     /* 07. 裝載子進程信號處理器 */  
  9.     /* 08. 設置默認系統屬性 */  
  10.     /* 09. 啟動配置屬性的服務端 */  
  11.     /* 10. 匹配命令和函數之間的對應關系 */  
  12.     /* 11. 解析init.rc*/  
  13.     /* 12. 向執行隊列中添加其他action */  
  14. -------------------------------------------------------------------  
  15.     /* 13. 處理添加到運行隊列的事件 */  
  16.     while (true) {  
  17.     // 判斷是否有事件需要處理  
  18.         if (!waiting_for_exec) {  
  19.             // 依次執行每個action中攜帶command對應的執行函數  
  20.      am.ExecuteOneCommand();  
  21.         // 重啟一些掛掉的進程  
  22.             restart_processes();  
  23.         }  
  24.   
  25.         // 以下決定timeout的時間,將影響while循環的間隔  
  26.         int timeout = -1;  
  27.         // 有進程需要重啟時,等待該進程重啟  
  28.         if (process_needs_restart) {  
  29.             timeout = (process_needs_restart - gettime()) * 1000;  
  30.             if (timeout < 0)  
  31.                 timeout = 0;  
  32.         }  
  33.   
  34.         // 有action待處理,不等待  
  35.         if (am.HasMoreCommands()) {  
  36.             timeout = 0;  
  37.         }  
  38.   
  39.         // bootchart_sample應該是進行性能數據采樣  
  40.         bootchart_sample(&timeout);  
  41.   
  42.         epoll_event ev;  
  43.         // 沒有事件到來的話,最多阻塞timeout時間  
  44.         int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));  
  45.         if (nr == -1) {  
  46.             ERROR("epoll_wait failed: %s\n", strerror(errno));  
  47.         } else if (nr == 1) {  
  48.             //有事件到來,執行對應處理函數  
  49.             //根據上文知道,epoll句柄(即epoll_fd)主要監聽子進程結束,及其它進程設置系統屬性的請求  
  50.             ((void (*)()) ev.data.ptr)();  
  51.         }  
  52.     }  
  53.     return 0;  
  54. // end main  

  看一下ExecuteOneComand()函數:同樣位于system/core/init/action.cpp

Java代碼
  1. void ActionManager::ExecuteOneCommand() {  
  2.     // Loop through the trigger queue until we have an action to execute  
  3.     // 當前的可執行action隊列為空, trigger_queue_隊列不為空  
  4.     while (current_executing_actions_.empty() && !trigger_queue_.empty()) {  
  5.     // 循環遍歷action_隊列,包含了所有需要執行的命令,解析init.rc獲得  
  6.         for (const auto& action : actions_) {  
  7.             // 獲取隊頭的trigger, 檢查actions_列表中的action的trigger,對比是否相同  
  8.             if (trigger_queue_.front()->CheckTriggers(*action)) {  
  9.                 // 將所有具有同一trigger的action加入當前可執行action隊列  
  10.                 current_executing_actions_.emplace(action.get());  
  11.             }  
  12.         }  
  13.         // 將隊頭trigger出棧  
  14.         trigger_queue_.pop();  
  15.     }  
  16.   
  17.     if (current_executing_actions_.empty()) {   // 當前可執行的actions隊列為空就返回  
  18.         return;  
  19.     }  
  20.   
  21.     auto action = current_executing_actions_.front(); // 獲取當前可執行actions隊列的首個action  
  22.   
  23.     if (current_command_ == 0) {  
  24.         std::string trigger_name = action->BuildTriggersString();  
  25.         INFO("processing action (%s)\n", trigger_name.c_str());  
  26.     }  
  27.   
  28.     action->ExecuteOneCommand(current_command_);     // 執行當前的命令  
  29.   
  30.     // If this was the last command in the current action, then remove  
  31.     // the action from the executing list.  
  32.     // If this action was oneshot, then also remove it from actions_.  
  33.     ++current_command_;      // 不斷疊加,將action_中的所有命令取出  
  34.     if (current_command_ == action->NumCommands()) {  
  35.         current_executing_actions_.pop();  
  36.         current_command_ = 0;  
  37.         if (action->oneshot()) {  
  38.             auto eraser = [&action] (std::unique_ptr<Action>& a) {  
  39.                 return a.get() == action;  
  40.             };  
  41.             actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));  
  42.         }  
  43.     }  
  44. }  

  我們來觀察一下init.rc的開頭部分:

Java代碼
  1. import /init.environ.rc  
  2. import /init.usb.rc  
  3. import /init.${ro.hardware}.rc  
  4. import /init.usb.configfs.rc  
  5. import /init.${ro.zygote}.rc      // 后面我們即將重點分析zygote進程  

  通過ro.zygote的屬性import對應的zygote的rc文件。

Android啟動篇 — init原理(二)

  我們查看init.zygote64_32.rc:

XML/HTML代碼
  1. service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote  
  2.     class main  
  3.     socket zygote stream 660 root system  
  4.     onrestart write /sys/android_power/request_state wake  
  5.     onrestart write /sys/power/state on  
  6.     onrestart restart audioserver  
  7.     onrestart restart cameraserver  
  8.     onrestart restart media  
  9.     onrestart restart netd  
  10.     writepid /dev/cpuset/foreground/tasks  
  11.   
  12. service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary  
  13.     class main  
  14.     socket zygote_secondary stream 660 root system  
  15.     onrestart restart zygote  
  16.     writepid /dev/cpuset/foreground/tasks  

  可以看到zygote的class是main, 它是在on nonencrypted時被啟動的,如下:

XML/HTML代碼
  1. on boot  
  2.     # basic network init  
  3.     ifup lo  
  4.     hostname localhost  
  5.     domainname localdomain  
  6.     ... ...  
  7.     class_start core  
  8.   
  9. on nonencrypted  
  10.     # A/B update verifier that marks a successful boot.  
  11.     exec - root cache -- /system/bin/update_verifier nonencrypted  
  12.     class_start main  
  13.     class_start late_start  

  至此,Init.cpp的main函數分析完畢!init進程已經啟動完成,一些重要的服務如core服務和main服務也都啟動起來,并啟動了zygote(/system/bin/app_process64)進程,zygote初始化時會創建虛擬機,啟動systemserver等。

本文發布:Android開發網
本文地址:http://www.ojizl5.fun/android/course/712.html
2017年9月11日
發布:雞啄米 分類:Android開發教程 瀏覽: 評論:0