Android Q Init进程执行rc文件内容的流程分析

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章