Android 系统启动之 Init 进程启动分析五

本文基于 AOSP android-10.0.0_r41 版本讲解,内核版本 android-goldfish-4.14-gchips

上一节我们看了 init.rc 的格式分析,接下来我们就来看看,代码中是如何解析 init.rc 文件的。

我们接着看 SecondStageMain 后续的代码:

cpp 复制代码
int SecondStageMain(int argc, char** argv) {

    // ......

    // 构建内置函数映射表对象,用于处理 rc 文件中 action 的各个命令
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);

    // 这个主要设置 ./apex 这些分区的挂载信息权限的。这块可以参考资料:https://cizixs.com/2017/08/29/linux-namespace/
    if (!SetupMountNamespaces()) {
        PLOG(FATAL) << "SetupMountNamespaces failed";
    }

    
    // 一个容器, 记录u:r:init:s0和u:r:vendor_init:s0类型的安全上下文
    // android P 版本以上,给 vendor oem 增加 u:r:vendor_init:s0 权限
    subcontexts = InitializeSubcontexts();

    // 构建 ActionManager 对象,用于管理和调度各个 Action
    ActionManager& am = ActionManager::GetInstance();
    // 构建 ServiceList 对象,用于管理和调度各个 Service
    ServiceList& sm = ServiceList::GetInstance();

    // 加载各种 rc 文件, 优先加载 bootargs 中 androidboot.init_rc=xxx 指定的 rc 文件
    // 一般很少指定这个, 所有都是按照如下顺序去加载 rc 文件
    // /init.rc->/system/etc/init目录rc->/product/etc/init目录rc-->/product_services/etc/init
    // -->/odm/etc/init --> /vendor/etc/init
    LoadBootScripts(am, sm);
    // .....

这部分代码,我们主要关注最后部分,先获取两个单例对象 ActionManager 和 ServiceList,接着将两个对象传入 LoadBootScripts。

LoadBootScripts 函数主要工作是加载各种 rc 文件:

cpp 复制代码
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    // 创建脚本解析器
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    // 若 ro.boot.init_rc 属性没有设置具体的 init.rc 文件,则进入下面逻辑
    if (bootscript.empty()) {
        // 解析 /init.rc 文件,会把 on 关键字解析为 Action 对象,会把 service 关键字解析为 Service 对象
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/product_services/etc/init")) {
            late_import_paths.emplace_back("/product_services/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

这里先调用 CreateParser 函数创建了一个 Parser 对象,该对象用于解析我们的 init.rc 文件:

cpp 复制代码
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;

    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

    return parser;
}

这里初始化了一个 parser,并通过 AddSectionParser 函数给 parser 添加了 ServiceParser ActionParser ImportParser 三个成员:

  • ServiceParser 用于解析 init.rc 中定义的 Service
  • ActionParser 用于解析 init.rc 中定义的 Action
  • ImportParser 用于解析 init.rc 中的 import 语句

他们的关系如下:

LoadBootScripts 函数接下来会优先加载 bootargs 中 androidboot.init_rc=xxx 指定的 rc 文件,一般很少指定这个, 所有都是按照如下顺序去加载 rc 文件:

  • /init.rc
  • /system/etc/init 目录
  • /product/etc/init 目录
  • /product_services/etc/init 目录
  • /odm/etc/init 目录
  • /vendor/etc/init 目录

我们先把解析过程放一边,先看看 Service 和 Action 解析出来后是什么样子:

Action 在 init.rc 中的样子:

bash 复制代码
# system/core/rootdir/init.rc
# action
on init
    sysclktz 0

    # Mix device-specific information into the entropy pool
    copy /proc/cmdline /dev/urandom
    copy /system/etc/prop.default /dev/urandom
    # ......

通过 Parser 的解析,它会变成一个 Action 对象:

cpp 复制代码
class Action {
    // 省略成员函数
    // 属性 trigger
    std::map<std::string, std::string> property_triggers_;
    // 事件 trigger
    std::string event_trigger_;
    // 命令
    std::vector<Command> commands_;
    bool oneshot_;
    Subcontext* subcontext_;
    std::string filename_;
    int line_;
    // 命令与对应函数的映射表
    static const KeywordFunctionMap* function_map_;
};

Service 在 init.rc 中的样子:

bash 复制代码
# frameworks/native/cmds/servicemanager/servicemanager.rc
# service
service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart audioserver
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart inputflinger
    onrestart restart drm
    onrestart restart cameraserver
    onrestart restart keystore
    onrestart restart gatekeeperd
    onrestart restart thermalservice
    writepid /dev/cpuset/system-background/tasks
    shutdown critical

通过 Parser 的解析,它会变成一个 Service 对象:

cpp 复制代码
class Service {
  
    // 省略成员函数
    static unsigned long next_start_order_;
    static bool is_exec_service_running_;

    // 服务名
    std::string name_;
    // 服务对于的 class 名
    std::set<std::string> classnames_;
    // 终端
    std::string console_;

    unsigned flags_;
    pid_t pid_;
    android::base::boot_clock::time_point time_started_;  // time of last start
    android::base::boot_clock::time_point time_crashed_;  // first crash within inspection window
    int crash_count_;                     // number of times crashed within window

    uid_t uid_;
    gid_t gid_;
    std::vector<gid_t> supp_gids_; 
    std::optional<CapSet> capabilities_;
    unsigned namespace_flags_;
    // Pair of namespace type, path to namespace.
    std::vector<std::pair<int, std::string>> namespaces_to_enter_;

    // selinux 安全上下文
    std::string seclabel_;

    std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
    std::vector<std::pair<std::string, std::string>> environment_vars_;

    Action onrestart_;  // Commands to execute on restart.

    std::vector<std::string> writepid_files_;

    std::set<std::string> interfaces_;  // e.g. some.package.foo@1.0::IBaz/instance-name

    // keycodes for triggering this service via /dev/input/input*
    std::vector<int> keycodes_;

    IoSchedClass ioprio_class_;
    int ioprio_pri_;
    int priority_;

    int oom_score_adjust_;

    int swappiness_ = -1;
    int soft_limit_in_bytes_ = -1;

    int limit_in_bytes_ = -1;
    int limit_percent_ = -1;
    std::string limit_property_;

    bool process_cgroup_empty_ = false;

    bool override_ = false;

    unsigned long start_order_;

    std::vector<std::pair<int, rlimit>> rlimits_;

    bool sigstop_ = false;

    std::chrono::seconds restart_period_ = 5s;
    std::optional<std::chrono::seconds> timeout_period_;

    bool updatable_ = false;

    std::vector<std::string> args_;

    std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;

    bool pre_apexd_ = false;

    bool post_data_ = false;

    bool running_at_post_data_reset_ = false;
};

Service 的成员很多,我们可以在使用的时候在来看每个成员的作用。

知道了解析前后的样子,我们就来看看 Parser 的解析过程:

cpp 复制代码
bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
        return ParseConfigDir(path);
    }
    return ParseConfigFile(path);
}

bool Parser::ParseConfigFile(const std::string& path) {
    LOG(INFO) << "Parsing file " << path << "...";
    android::base::Timer t;
    // 文件内容读出,存在字符串中
    auto config_contents = ReadFile(path);
    if (!config_contents) {
        LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
        return false;
    }

    ParseData(path, &config_contents.value());

    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
    return true;
}

void Parser::ParseData(const std::string& filename, std::string* data) {
    data->push_back('\n');  // TODO: fix tokenizer
    data->push_back('\0');

    parse_state state;
    state.line = 0;
    state.ptr = data->data();
    state.nexttoken = 0;

    SectionParser* section_parser = nullptr;
    int section_start_line = -1;
    std::vector<std::string> args;

    // If we encounter a bad section start, there is no valid parser object to parse the subsequent
    // sections, so we must suppress errors until the next valid section is found.
    bool bad_section_found = false;

    auto end_section = [&] {
        bad_section_found = false;
        if (section_parser == nullptr) return;

        if (auto result = section_parser->EndSection(); !result) {
            parse_error_count_++;
            LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
        }

        section_parser = nullptr;
        section_start_line = -1;
    };

    for (;;) {
        switch (next_token(&state)) {
            case T_EOF:
                end_section();

                for (const auto& [section_name, section_parser] : section_parsers_) {
                    section_parser->EndFile();
                }

                return;
            case T_NEWLINE: {
                state.line++;
                if (args.empty()) break;
                // If we have a line matching a prefix we recognize, call its callback and unset any
                // current section parsers.  This is meant for /sys/ and /dev/ line entries for
                // uevent.
                //解析一行,并把结果放到 args 中
                auto line_callback = std::find_if(
                    line_callbacks_.begin(), line_callbacks_.end(),
                    [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });
                if (line_callback != line_callbacks_.end()) {
                    end_section();

                    if (auto result = line_callback->second(std::move(args)); !result) {
                        parse_error_count_++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                    }
                } else if (section_parsers_.count(args[0])) {
                    end_section();
                    section_parser = section_parsers_[args[0]].get();
                    section_start_line = state.line;
                    if (auto result =
                            section_parser->ParseSection(std::move(args), filename, state.line);
                        !result) {
                        parse_error_count_++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        section_parser = nullptr;
                        bad_section_found = true;
                    }
                } else if (section_parser) {
                    if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                        !result) {
                        parse_error_count_++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                    }
                } else if (!bad_section_found) {
                    parse_error_count_++;
                    LOG(ERROR) << filename << ": " << state.line
                               << ": Invalid section keyword found";
                }
                args.clear();
                break;
            }
            case T_TEXT:
                args.emplace_back(state.text);
                break;
        }
    }
} 

对于 Service 会调用到 ServiceParser 对象的 ParseSection ParseLineSection EndSection 等函数将 init.rc 中的文本配置信息转换为 Service 对象,并把 Service 保存到 ServerList 中。

对于 Actiong 会调用到 ActionParser 对象的 ParseSection ParseLineSection EndSection 等函数将 init.rc 中的文本配置信息转换为 Action 对象,并把 Action 保存到 ActionManager 中。

实际开发,用 json xml 保存配置比较多(三方成熟库多),自定义格式会比较少,关于解析部分的代码有兴趣可以自己看看源码。

接着看 SecondStageMain 后续代码:

cpp 复制代码
int SecondStageMain(int argc, char** argv) {

    // ......

    // Turning this on and letting the INFO logging be discarded adds 0.2s to
    // Nexus 9 boot time, so it's disabled by default.
    if (false) DumpState();

    // Make the GSI status available before scripts start running.
    if (android::gsi::IsGsiRunning()) {
        property_set("ro.gsid.image_running", "1");
    } else {
        property_set("ro.gsid.image_running", "0");
    }

    // 代码中添加 build in Action
    am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");

    // 添加 erarly-init 到事件队列中
    // 注意此处并没有触发
    am.QueueEventTrigger("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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    Keychords keychords;
    am.QueueBuiltinAction(
        [&epoll, &keychords](const BuiltinArguments& args) -> Result<Success> {
            for (const auto& svc : ServiceList::GetInstance()) {
                keychords.Register(svc->keycodes());
            }
            keychords.Start(&epoll, HandleKeychord);
            return Success();
        },
        "KeychordInit");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    // 添加 erarly-init 到事件队列中,注意此处并没有触发
    am.QueueEventTrigger("init");

    // Starting the BoringSSL self test, for NIAP certification compliance.
    am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");

    // 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");

    // Initialize binder before bringing up other system services
    am.QueueBuiltinAction(InitBinder, "InitBinder");

    // 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");
    // ......
}
  • QueueBuiltinAction 构造了一个 Action 对象, 放到事件队列和动作队列中
  • QueueEventTrigger 添加一个事件到事件队列中,注意此处并没有触发事件

接着看 SecondStageMain 后续代码:

cpp 复制代码
int SecondStageMain(int argc, char** argv) {
    // ......
    while (true) {
        // By default, sleep until something happens.
        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};

        if (do_shutdown && !shutting_down) {
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {
                shutting_down = true;
            }
        }

        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            /*
            将按照 event_queue_ 的顺序,依次取出action链表中与trigger匹配的action。
            每次均执行一个action中的一个command对应函数(一个action可能携带多个command)。
            当一个action所有的command均执行完毕后,再执行下一个action。
            当一个trigger对应的action均执行完毕后,再执行下一个trigger对应action。
            */
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            if (!shutting_down) {
                auto next_process_action_time = HandleProcessActions();

                // If there's a process that needs restarting, wake up in time for that.
                if (next_process_action_time) {
                    epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                            *next_process_action_time - boot_clock::now());
                    if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
                }
            }

            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout = 0ms;
        }

        if (auto result = epoll.Wait(epoll_timeout); !result) {
            LOG(ERROR) << result.error();
        }
    }

    return 0;
}

这里的核心是执行 ExecuteOneCommand 函数:

cpp 复制代码
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_) {
            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;
    }

    // 拿到 action
    auto action = current_executing_actions_.front();

    if (current_command_ == 0) {
        // 拿到 trigger
        std::string trigger_name = action->BuildTriggersString();
        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
                  << ":" << action->line() << ")";
    }

    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_;
    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));
        }
    }
}

void Action::ExecuteOneCommand(std::size_t command) const {
    // We need a copy here since some Command execution may result in
    // changing commands_ vector by importing .rc files through parser
    Command cmd = commands_[command];
    ExecuteCommand(cmd);
}

void Action::ExecuteCommand(const Command& command) const {
    android::base::Timer t;
    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.
    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
    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());
    }
}

ExecuteOneCommand 函数将按照 event_queue_ 中的顺序,依次取出 action 链表中与 trigger 匹配的 action。 每次均执行一个 action 中的一个 command 对应函数(一个 action 可能携带多个 command)。当一个 action 所有的 command 均执行完毕后,再执行下一个 action。当一个 trigger 对应的 action 均执行完毕后,再执行下一个 trigger 对应action。

最后总结一下,Android 启动过程中重要的 trigger 事件如下:

bash 复制代码
- early-init
  +--- start ueventd
- init
- charger                       # 当 ro.bootmode 为 charger 执行
- late-init
  +--- early-fs
  +--- fs                       # 挂载系统分区
     +--- mount_all /vendor/etc/fstab.${ro.hardware}
  +--- post-fs                  # 执行依赖文件系统的命令
     +--- load_system_props
        +--- load_properties_from_file("/system/build.prop", NULL);
        +--- load_properties_from_file("/odm/build.prop", NULL);
        +--- load_properties_from_file("/vendor/build.prop", NULL);
        +--- load_properties_from_file("/factory/factory.prop", "ro.*");
        +--- load_recovery_id_prop();
     +--- start logd
     +--- start servicemanager
     +--- start hwservicemanager
     +--- start vndservicemanager
  +--- late-fs
     +--- class_start early_hal
  +--- post-fs-data              # data分区初始化
     +--- start vold
     +--- installkey /data
     +--- bootchart start
     +--- init_user0
  +--- zygote-start
     +--- exec_start update_verifier_nonencrypted
     +--- start netd
     +--- start zygote
     +--- start zygote_secondary
  +--- load_persist_props_action
     +--- load_persist_props
     +--- LoadPersistentProperties()
     +--- start logd
     +--- start logd-reinit
  +--- firmware_mounts_complete
     +--- rm /dev/.booting
  +--- early-boot
  +--- boot

  +--- property:sys.boot_completed=1
     +--- bootchart stop

参考资料

关于

我叫阿豪,2015 年本科毕业于国防科学技术大学指挥信息系统专业,毕业后从事信息化装备的研发工作,工作内容主要涉及 Android Framework 与 Linux Kernel。

如果你对 Android Framework 感兴趣或者正在学习 Android Framework,可以关注我的微信公众号和抖音,我会持续分享我的学习经验,帮助正在学习的你少走一些弯路。学习过程中如果你有疑问或者你的经验想要分享给大家可以添加我的微信,我拉你进技术交流群。

相关推荐
️ 邪神1 小时前
【Android、IOS、Flutter、鸿蒙、ReactNative 】标题栏
android·flutter·ios·鸿蒙·reatnative
努力遇见美好的生活2 小时前
Mysql每日一题(行程与用户,困难※)
android·数据库·mysql
图王大胜3 小时前
Android Framework AMS(17)APP 异常Crash处理流程解读
android·app·异常处理·ams·crash·binderdied·讣告
ch_s_t4 小时前
电子商务网站之首页设计
android
豆 腐6 小时前
MySQL【四】
android·数据库·笔记·mysql
想取一个与众不同的名字好难8 小时前
android studio导入OpenCv并改造成.kts版本
android·ide·android studio
Jewel1059 小时前
Flutter代码混淆
android·flutter·ios
Yawesh_best10 小时前
MySQL(5)【数据类型 —— 字符串类型】
android·mysql·adb
曾经的三心草13 小时前
Mysql之约束与事件
android·数据库·mysql·事件·约束
guoruijun_2012_417 小时前
fastadmin多个表crud连表操作步骤
android·java·开发语言