上一篇文章:Android Q Init進程解析 rc文件的流程分析 已經對init進程解析rc文件的流程進行了詳細分析。
下面我們就分析下init進程具體是如何執行這些rc文件中解析出來的內容的
一,執行之前的準備
init進程在解析完畢rc文件之後,就會立即開始準備執行rc文件的內容,在執行rc文件的內容之前,需要通過QueueEventTrigger和QueueBuiltinAction這兩個函數向待執行的action隊列中添加待執行的任務。
if (false) DumpState();
// rc 文件解析完畢之後,依次向am的待執行 action 隊列中添加需要執行的 action任務
// action 任務主要有下面三種 EventTrigger, PropertyChange, BuiltinAction
// 下面這些是按照添加的順序來依次執行的
// 這裏就是添加一個名稱爲early-init的EventTrigger
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
// 這裏就是添加一個名稱爲 wait_for_coldboot_done 的 BuiltinAction
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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
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");
// 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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
LOG(INFO) << "end QueueBuiltinAction and QueueEventTrigger goto while(true)";
while (true) {
上面代碼中從DumpState()到while (true)之前的全部代碼都是向待執行的action隊列中添加待執行的任務。
這裏兩個函數略有差別,我們先分別看下這兩個添加待執行任務的函數有何不同。
void ActionManager::QueueEventTrigger(const std::string& trigger) {
event_queue_.emplace(trigger);
}
// 這裏是添加內置任務的,init.cpp中給定了執行此任務所需要調用的函數func,
// 因此這裏首先根據傳遞進來的參數創建一個action對象,然後根據傳遞進來的參數func構建
// command並添加到action的Command列表中
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
std::map<std::string, std::string>{});
std::vector<std::string> name_vector{name};
action->AddCommand(func, name_vector, 0);
// 將當前任務添加到待執行任務列表中
event_queue_.emplace(action.get());
// 將當前action添加到action列表中
actions_.emplace_back(std::move(action));
}
void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
// std::make_pair 將 name 跟value 拼接成一個字符串
event_queue_.emplace(std::make_pair(name, value));
}
通過上面的代碼可以看下,這兩個函數最終都是向event_queue_中添加待執行的任務,只是QueueBuiltinAction函數需要先創建action,給創建的action添加command之後在添加到event_queue_中,QueueEventTrigger是直接構造action添加到event_queue_中(emplace會自動創建實例對象並添加到列表),這裏後面還有一個QueuePropertyChange,似乎也是添加任務到待執行列表中,這個後續在做梳理。
這裏我們重點看下event_queue_這個神祕的變量具體是在麼定義的。
class ActionManager {
public:
static ActionManager& GetInstance();
// Exposed for testing
ActionManager();
void AddAction(std::unique_ptr<Action> action);
void QueueEventTrigger(const std::string& trigger);
void QueuePropertyChange(const std::string& name, const std::string& value);
void QueueAllPropertyActions();
void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
void ExecuteOneCommand();
bool HasMoreCommands() const;
void DumpState() const;
void ClearQueue();
private:
ActionManager(ActionManager const&) = delete;
void operator=(ActionManager const&) = delete;
std::vector<std::unique_ptr<Action>> actions_;
// 定義名稱爲 event_queue_ 的 queue 這個queue中可以存放 EventTrigger, PropertyChange, BuiltinAction 三種的任一一個對象
// 保存待執行的action 列表
std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
std::queue<const Action*> current_executing_actions_;
std::size_t current_command_;
};
這裏我們可以看到event_queue_是一個隊列,特別的是,這個隊列中存放的元素的類型包含三種不同的類型,下面我們看下這三種不同類型的具體定義:
using EventTrigger = std::string;
using PropertyChange = std::pair<std::string, std::string>;
using BuiltinAction = class Action*;
class Action {
通過前面的這部分梳理,我們可以看到,init進程在準備工作中,已經將待執行的任務都添加到了event_queue_隊列中。然後就進入了while(true)的循環。下面我們看下這些待執行任務具體是如何執行的。
二,開始執行任務
while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
auto next_process_restart_time = RestartProcesses();
// If there's a process that needs restarting, wake up in time for that.
if (next_process_restart_time) {
epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_restart_time - boot_clock::now())
.count();
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
}
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
}
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
我們看到這個死循環裏面執行這些命令的入口就是am.ExecuteOneCommand();調用,下面我們就進入這個ExecuteOneCommand函數看下它的具體實現。‘
// 這個函數會在init進程的最後的while循環中反覆調用
void ActionManager::ExecuteOneCommand() {
// Loop through the event queue until we have an action to execute
// 當前正在執行的任務列表爲空,但是等待執行的任務列表不爲空
while (current_executing_actions_.empty() && !event_queue_.empty()) {
for (const auto& action : actions_) {
// 這個判斷的意思是,event_queue_中取出第一個元素,可能是 EventTrigger, PropertyChange, BuiltinAction 三種中的某一個
// 然後調action的CheckEvent函數,如果函數返回 true 就說明當前等待執行的任務列表(event_queue_)中的待執行任務與當前
// action 匹配。這個時候將當前action添加到正在執行的任務列表(current_executing_actions_)中
if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },event_queue_.front())) {
current_executing_actions_.emplace(action.get());
}
}
event_queue_.pop();
}
// 如果需要執行的任務列表爲空,就啥都不做,直接返回
if (current_executing_actions_.empty()) {
return;
}
// 取出待執行任務列表中的第一項任務
auto action = current_executing_actions_.front();
// current_command_ == 0 表示當前的action第一次執行
if (current_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()<< ":" << action->line() << ")";
// processing action (early-init) from (/vendor/etc/init/hw/init.modem.rc:7)
}
// 執行當前action任務集合中的第 current_command_ 條指令
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 中的指令數量,就說明這個 action執行完畢
if (current_command_ == action->NumCommands()) {
// 將當前action 出隊列
current_executing_actions_.pop();
current_command_ = 0; // 計數器歸零
if (action->oneshot()) { // 如果這個任務是一次性的,將當前 action 中 actingmanager的action 列表中擦除
auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
}
}
}
這個函數的代碼中有詳細的註釋,代碼流程比較清晰,首先它會遍歷待執行的任務列表,取出一個任務,然後逐條與之前已經解析出來的任務列表進行匹配,如果匹配上了,就從actions_中獲取匹配OK的action,添加到current_executing_actions_中準備執行。然後在while循環結束之後。依次取出current_executing_actions_中等待執行的action列表,依次執行它的每一項命令。
注意(這裏由於init.cpp 中的while會一直循環,因此這個ExecuteOneCommand函數會被反覆調用,直到event_queue_爲空)
下面我們來分析下 action->ExecuteOneCommand(current_command_);的具體調用流程。
// 執行具體的指令,
void Action::ExecuteCommand(const Command& command) const {
android::base::Timer t;
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
LOG(INFO) << "ExecuteCommand '" << cmd_str << "' action=" << trigger_name;
auto result = command.InvokeFunc(subcontext_); // 關鍵地方,執行當前命令
auto duration = t.duration();
// There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
// device, such as '/sys/class/leds/jogball-backlight/brightness'. As of this writing, there
// are 198 such failures on bullhead. Instead of spamming the log reporting them, we do not
// report such failures unless we're running at the DEBUG log level.
// 因正常情況導致的操作失敗,不能當做失敗來打印log
bool report_failure = !result.has_value();
if (report_failure && android::base::GetMinimumLogSeverity() > android::base::DEBUG &&
result.error_errno() == ENOENT) {
report_failure = false;
}
// Any action longer than 50ms will be warned to user as slow operation
// 執行失敗的,耗時長的,或者當前版本是debug版本的,需要打印命令的執行結果
if (report_failure || duration > 50ms ||
android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
// std::string trigger_name = BuildTriggersString();
// std::string cmd_str = command.BuildCommandString();
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
<< ":" << command.line() << ") took " << duration.count() << "ms and "
<< (result ? "succeeded" : "failed: " + result.error_string());
}
}
這裏直接調用了command.InvokeFunc(subcontext_)進一步執行,然後就是對執行結果的檢查。下面我們來看下InvokeFunc的具體實現。
Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
if (subcontext) {
/*
凡是從/vendor/ 或者 /odm/ 解析出來的rc文件都會走這裏。
這裏涉及到不同的selinux權限,因爲這兩個目錄都是廠商定製目錄,谷歌爲了確保安全,
對廠商定製的rc文件中的命令執行,已經由此啓動的服務的權限會有一定的限制(通過selinux來限制)
可以查看下面的兩個鏈接瞭解詳情:
https://blog.csdn.net/vicluo/article/details/103186032
https://source.android.google.cn/security/selinux/vendor-init
*/
if (execute_in_subcontext_) {
return subcontext->Execute(args_);
}
auto expanded_args = subcontext->ExpandArgs(args_);
if (!expanded_args) {
return expanded_args.error();
}
return RunBuiltinFunction(func_, *expanded_args, subcontext->context());
}
// 所以系統原生的命令執行都會走到這裏
return RunBuiltinFunction(func_, args_, kInitContext);
}
Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
const std::vector<std::string>& args,
const std::string& context) {
auto builtin_arguments = BuiltinArguments(context);
builtin_arguments.args.resize(args.size());
builtin_arguments.args[0] = args[0];
for (std::size_t i = 1; i < args.size(); ++i) {
if (!expand_props(args[i], &builtin_arguments.args[i])) {
return Error() << "cannot expand '" << args[i] << "'";
}
LOG(INFO) << "RunBuiltinFunction i = "<<i<< " args[i] = "<< args[i]<<" builtin_arguments.args[i] = "+builtin_arguments.args[i];
}
// 這裏會調用從前面的map中查詢到的函數,並將參數傳遞過去
// 比如 執行 init.rc 中的 start zygote_secondary 這個操作,
// 會首先根據 start 查詢到其對應的處理函數是 do_start() 這個函數,而builtin_arguments就作爲參數傳遞給了do_start這個函數
return function(builtin_arguments);
}
’代碼流程最終到了RunBuiltinFunction函數中,而這個函數就是準備參數,然後調用執行當前命令所需要的具體的執行函數。
由於每一個執行操作的具體函數的實現都不一樣,因此這裏就不一一分析了,這些代碼的具體實現在builtins.cpp中,大家可以自己看下。
至此init執行rc文件中解析出來的命令的流程就分析完了。
後續我們會繼續通過init啓動zygote的具體流程來分析zygote的詳細啓動流程。