【前言】
安卓init:Android7.0系統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. 匹配命令和函數之間的對應關係 */ ------------------------------------------------------------------------------------------- // 安卓init:Android7.0系統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等。