上一篇文章: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的详细启动流程。