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的詳細啓動流程。

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