科技

Android启动流程之二 init.rc解析

2020-04-05  本文已影响0人  罗公子Coding

一、init.rc

Android中利用rc文件配置一些需要提前执行的操作,在系统启动的时候解析并执行,为启动Android系统核心服务提供保障。可参考:http://androidxref.com/9.0.0_r3/xref/system/core/init/README.md

rc文件以行为单位,一行定义一个语句,使用#作为注释。rc语法核心包括

其中需要注意的是Action和Service的名称是唯一的。
rc文件是可以通过import语句来导入其他rc文件的,例如/init.rc就引入包含了其他文件


import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc

1.1、 Action

以on开头,通过触发器trigger来决定对应的service什么时候执行,执行的时机有

on <trigger> #触发条件
    <command> #触发命令
    <command1>#第二个触发命令,可以执行多个命令

1.2、Service

Service,顾名思义就是需要启动的服务,以service开头,init启动后会由init进程启动它作为子进程。
定义:

service <name> <pathname> [<argument>]*  #可以有多个参数
    <option>
    <option> #可以有多个option

例子

service servicemanager /system/bin/servicemanager

定义的就是名称为servicemanager的服务,对应的执行文件为system/bin/servicemanager,因此在启动服务前需要判断服务对应的文件是否存在

1.3、Command

Command主要是一些常用的操作命令,例如:

1.4、Options

Option是Service配合使用的可选操作项

1.5 demo

#初始化早期执行
on early-init
    # Set init and its forked children's oom_adj.
    # 往/proc/1/oom_score_adj中写入 -1000
    write /proc/1/oom_score_adj -1000
    #启动ueventd服务
    start ueventd

#初始化时执行
on init
  mkdir /dev/stune
  write /proc/sys/kernel/sched_tunable_scaling 0

#当sys.boot_from_charger_mode为1时执行
on property:sys.boot_from_charger_mode=1
    class_stop charger
    trigger late-init

#初始化后期执行
on late-init
    trigger early-fs

# 定义一个名为flash_recovery,执行文件在/system/bin/install-recovery.sh的服务
# 该服务是oneshot的,执行完成后不再重启,且启动或者退出动作和main服务一起
service flash_recovery /system/bin/install-recovery.sh
    class main
    oneshot

二、init.rc解析

《Android启动流程之一 init进程启动》中有提到,init进程的main函数中有加载init.rc文件,对应的执行函数为:

2.1、LoadBootScripts

先判断是否自定义了rc文件,如果没有的话就读取默认的/init.rc

system/core/init/init.cpp

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    //创建解析器
    Parser parser = CreateParser(action_manager, service_list);
    //先判断是否自定义了rc文件,如果没有的话就读取默认的/init.rc
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        //解析/init.rc
        parser.ParseConfig("/init.rc");
        //以下这些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("/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 {
        //解析自定义的rc文件
        parser.ParseConfig(bootscript);
    }
}

这个过程中最主要的一个操作是,调用CreateParser方法创建了rc文件的解析器,有三种解析器,分别是:

类图为:


Parser解析器类图

Parser中主要有四个关键方法:

2.2、ParseConfig & ParseData方法

ParseConfig会调用两个参数的ParseConfig,这里会判断传入的path是文件还是目录,如果是文件的话调用ParseConfigFile解析,如果是目录的话调用ParseConfigDir遍历目录下的rc文件全部解析,这个步骤可以忽略不用太关注,最终都会调用到ParseData方法真正解析文件,整个解析过程会扫描配置文件,解析指令存放到链表中,在init进程中执行。
ParseData函数

system/core/init/parser.cpp

//第一个参数:文件路径,第二个参数:文件内容,第三个参数:错误信息
void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
    // TODO: Use a parser with const input and remove this copy
    //开始之前为了安全考虑,先复制一份文件内容进行解析
    std::vector<char> data_copy(data.begin(), data.end());
    //在读取到的数据最后添加一个‘\0’
    data_copy.push_back('\0');

    parse_state state;
    //初始化从第0行
    state.line = 0;
    //开始指针指向0
    state.ptr = &data_copy[0];
    state.nexttoken = 0;

    SectionParser* section_parser = nullptr;
    int section_start_line = -1;
    //存放读取到的配置文本
    std::vector<std::string> args;

    auto end_section = [&] {
        if (section_parser == nullptr) return;
        //调用EndSection方法保存读取到的配置信息
        if (auto result = section_parser->EndSection(); !result) {
            (*parse_errors)++;
            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();
                return;
            case T_NEWLINE://解析完一行,新开始一行的解析
                //开始读取新的一行,line加1
                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.
                //这里主要是针对ueventd.rc文件的处理,识别 /sys/ /dev/开头的配置
                for (const auto& [prefix, callback] : line_callbacks_) {
                    if (android::base::StartsWith(args[0], prefix)) {
                        end_section();

                        if (auto result = callback(std::move(args)); !result) {
                            (*parse_errors)++;
                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        }
                        break;
                    }
                }
                if (section_parsers_.count(args[0])) {
                    //切换parser之前先确保上一次的解析完成
                    end_section();
                    //args[0]表示是section的开头文本,也就是service,on,import三者之一
                    //依据这个来匹配到对应的Parser
                    section_parser = section_parsers_[args[0]].get();
                    section_start_line = state.line;
                    //解析Action关键字行
                    if (auto result =
                            section_parser->ParseSection(std::move(args), filename, state.line);
                        !result) {
                        (*parse_errors)++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        section_parser = nullptr;
                    }
                } else if (section_parser) {
                    //解析Action对应的命令行
                    if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                        !result) {
                        (*parse_errors)++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                    }
                }
                args.clear();
                break;
            case T_TEXT://解析到字符串
                //读取到文本就添加到args中,一行保存一次
                //state是一个结构体
                //struct parse_state
                //{
                //  char *ptr;      // 要解析的字符串
                //  char *text;     // 解析到的字符串,可以理解为返回一行的数据
                //  int line;       // 解析到第行数
                //  int nexttoken;  // 解析状态,有 T_EOF、T_NEWLINE、T_TEXT 三种
                //};
                //
                //
                args.emplace_back(state.text);
                break;
        }
    }
}

ParseData函数中我们需要注意以下几点

struct parse_state{
   char *ptr; //要解析的字符串
   char *text;//解析到的字符串,一般为一行字符串
   int line; //解析到第几行
   int nexttoken; //解析状态,有 T_EOF、T_NEWLINE、T_TEXT 三种
}

2.3、ActionParser

上面有提供Parser的类图大致结构,在解析过程中实际的解析会在对应的Parser中进行。ActionParser主要是解析以 on开头的一系列命令,Action决定对应的动作什么时候会执行,解析完成后Action会被添加到存放Action的队列中。Action的每个命令是按顺序排队执行,每个命令依次进入执行状态。
ActionParser实际上就是把配置解析为Action结构,对应的Action类定义在system/core/init/action.h中

    ......
    std::map<std::string, std::string> property_triggers_;
    std::string event_trigger_;
    std::vector<Command> commands_;
    ......

Commands存放Action中定义的一系列Command操作。

2.3.1 ParseSection

Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
                                           const std::string& filename, int line) {
    //这里判断Action是否有定义对应的trigger触发器,如果没有的话就返回失败
    std::vector<std::string> triggers(args.begin() + 1, args.end());
    if (triggers.size() < 1) {
        return Error() << "Actions must have a trigger";
    }

   ......

    std::string event_trigger;
    std::map<std::string, std::string> property_triggers;
    //解析Action的trigger触发器,提取event_trigger和property_triggers
    if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
        !result) {
        return Error() << "ParseTriggers() failed: " << result.error();
    }
    //创建Action对象
    auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
                                           property_triggers);

    action_ = std::move(action);
    return Success();
}

这里传递给ParseTriggers方法的triggers是去除了命令关键字on之外的触发器信息,ParseTriggers会判断是否是属性变化触发的触发器,如果是属性变化的触发器的话填充到property_triggers,否则填充到event_trigger。


Result<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
                              std::string* event_trigger,
                              std::map<std::string, std::string>* property_triggers) {
    const static std::string prop_str("property:");
    for (std::size_t i = 0; i < args.size(); ++i) {
        if (args[i].empty()) {
            return Error() << "empty trigger is not valid";
        }
        //trigger触发器需要通过 && 连接
        //例如:on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
        if (i % 2) {
            if (args[i] != "&&") {
                return Error() << "&& is the only symbol allowed to concatenate actions";
            } else {
                continue;
            }
        }
        //判断是否是property:开头的配置条件,Action中满足条件时会被执行,这里主要是属性变化触发器的解析
        //这里会把属性变化的触发条件填充到property_triggers中
        if (!args[i].compare(0, prop_str.length(), prop_str)) {
            if (auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers);
                !result) {
                return result;
            }
        } else {
            if (!event_trigger->empty()) {
                return Error() << "multiple event triggers are not allowed";
            }
            //如果不是属性变化的触发器,则填充到event_trigger中
            *event_trigger = args[i];
        }
    }

    return Success();
}

ParseTriggers做了两件事情

2.3.2、ParseLineSection

解析完成命令关键字部分后就会解析对应的Command子模块,ParseLineSection直接调用了Action的AddCommand函数


Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
    if (!function_map_) {
        return Error() << "no function map available";
    }
    //通过args调用FindFunction查找对应的处理命令,类似解析命令关键字行的做法
    //利用args[0]进行匹配,function_map在init进程启动的时候已经初始化了。
    auto function = function_map_->FindFunction(args);
    if (!function) return Error() << function.error();

    commands_.emplace_back(function->second, function->first, args, line);
    return Success();
}

FindFunction函数在keyword_map.h中会通过args[0]以及具体的字符内容匹配到最终的命令参数,最后填充到commands_中
function_map_的内容如下(system/core/init/builtins.cpp)

 static const Map builtin_functions = {
        {"bootchart",               {1,     1,    {false,  do_bootchart}}},
        {"chmod",                   {2,     2,    {true,   do_chmod}}},
        {"chown",                   {2,     3,    {true,   do_chown}}},
        {"class_reset",             {1,     1,    {false,  do_class_reset}}},
        {"class_restart",           {1,     1,    {false,  do_class_restart}}},
        {"class_start",             {1,     1,    {false,  do_class_start}}},
        {"class_stop",              {1,     1,    {false,  do_class_stop}}},
        {"copy",                    {2,     2,    {true,   do_copy}}},
        {"domainname",              {1,     1,    {true,   do_domainname}}},
        {"enable",                  {1,     1,    {false,  do_enable}}},
        {"exec",                    {1,     kMax, {false,  do_exec}}},
        {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
        {"exec_start",              {1,     1,    {false,  do_exec_start}}},
        {"export",                  {2,     2,    {false,  do_export}}},
        {"hostname",                {1,     1,    {true,   do_hostname}}},
        {"ifup",                    {1,     1,    {true,   do_ifup}}},
        {"init_user0",              {0,     0,    {false,  do_init_user0}}},
        {"insmod",                  {1,     kMax, {true,   do_insmod}}},
        {"installkey",              {1,     1,    {false,  do_installkey}}},
        {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
        {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
        {"loglevel",                {1,     1,    {false,  do_loglevel}}},
        {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
        // TODO: Do mount operations in vendor_init.
        // mount_all is currently too complex to run in vendor_init as it queues action triggers,
        // imports rc scripts, etc.  It should be simplified and run in vendor_init context.
        // mount and umount are run in the same context as mount_all for symmetry.
        {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
        {"mount",                   {3,     kMax, {false,  do_mount}}},
        {"umount",                  {1,     1,    {false,  do_umount}}},
        {"readahead",               {1,     2,    {true,   do_readahead}}},
        {"restart",                 {1,     1,    {false,  do_restart}}},
        {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
        {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
        {"rm",                      {1,     1,    {true,   do_rm}}},
        {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
        {"setprop",                 {2,     2,    {true,   do_setprop}}},
        {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
        {"start",                   {1,     1,    {false,  do_start}}},
        {"stop",                    {1,     1,    {false,  do_stop}}},
        {"swapon_all",              {1,     1,    {false,  do_swapon_all}}},
        {"symlink",                 {2,     2,    {true,   do_symlink}}},
        {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
        {"trigger",                 {1,     1,    {false,  do_trigger}}},
        {"verity_load_state",       {0,     0,    {false,  do_verity_load_state}}},
        {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
        {"wait",                    {1,     2,    {true,   do_wait}}},
        {"wait_for_prop",           {2,     2,    {false,  do_wait_for_prop}}},
        {"write",                   {2,     2,    {true,   do_write}}},
        #ifdef VENDOR_EDIT
        {"reload_policy",           {0,     0,    {true,   do_reload_policy}}},
        {"md",                      {1,     1,    {true,   do_md}}},
        {"copyall",                 {2,     2,    {true,   do_copyall}}},
        #if OP_FEATURE_UPDATE_RESERVE == 1
        {"mount_reserve",           {3,     kMax, {false,  do_mount_reserve}}},
        #endif
        #endif
    };

map对应的各字段,从左到右

2.3.3、EndSection

这里主要是将最终解析出来的Action保存到ActionManager的actions_中


Result<Success> ActionParser::EndSection() {
    if (action_ && action_->NumCommands() > 0) {
        action_manager_->AddAction(std::move(action_));
    }

    return Success();
}

void ActionManager::AddAction(std::unique_ptr<Action> action) {
    actions_.emplace_back(std::move(action));
}

2.4、ServiceParser

Service结构比较复杂,感兴趣的话可以阅读system/core/init/service.h文件。但是主要还是定义了Service的一些操作函数,rc文件中定义的一些命令和Options

2.4.1、ParseSection

Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
                                            const std::string& filename, int line) {
    //首先需要验证service配置是否正确,service配置必须包含
    //服务名和启动参数
    if (args.size() < 3) {
        return Error() << "services must have a name and a program";
    }

    const std::string& name = args[1];
    //验证服务名是否合法
    if (!IsValidName(name)) {
        return Error() << "invalid service name '" << name << "'";
    }

   ......
    //服务启动参数
    std::vector<std::string> str_args(args.begin() + 2, args.end());
    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
    return Success();
}

2.4.2、ParseLineSection

ParseLineSection主要是解析service命令快接下来Options的指令块。


Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
    return service_ ? service_->ParseLine(std::move(args)) : Success();
}


Result<Success> Service::ParseLine(const std::vector<std::string>& args) {
    static const OptionParserMap parser_map;
    auto parser = parser_map.FindFunction(args);

    if (!parser) return parser.error();

    return std::invoke(*parser, this, args);
}

parser_map是固定的,内容如下,参数分别代表:处理关键字、最小参数个数、最大参数个数、处理函数地址。:

static const Map option_parsers = {
        {"capabilities",
                        {1,     kMax, &Service::ParseCapabilities}},
        {"class",       {1,     kMax, &Service::ParseClass}},
        {"console",     {0,     1,    &Service::ParseConsole}},
        {"critical",    {0,     0,    &Service::ParseCritical}},
        {"disabled",    {0,     0,    &Service::ParseDisabled}},
        {"enter_namespace",
                        {2,     2,    &Service::ParseEnterNamespace}},
        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
        {"interface",   {2,     2,    &Service::ParseInterface}},
        {"ioprio",      {2,     2,    &Service::ParseIoprio}},
        {"priority",    {1,     1,    &Service::ParsePriority}},
        {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
        {"oneshot",     {0,     0,    &Service::ParseOneshot}},
        {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
        {"override",    {0,     0,    &Service::ParseOverride}},
        {"oom_score_adjust",
                        {1,     1,    &Service::ParseOomScoreAdjust}},
        {"memcg.swappiness",
                        {1,     1,    &Service::ParseMemcgSwappiness}},
        {"memcg.soft_limit_in_bytes",
                        {1,     1,    &Service::ParseMemcgSoftLimitInBytes}},
        {"memcg.limit_in_bytes",
                        {1,     1,    &Service::ParseMemcgLimitInBytes}},
        {"namespace",   {1,     2,    &Service::ParseNamespace}},
        {"rlimit",      {3,     3,    &Service::ParseProcessRlimit}},
        {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
        {"setenv",      {2,     2,    &Service::ParseSetenv}},
        {"shutdown",    {1,     1,    &Service::ParseShutdown}},
        {"socket",      {3,     6,    &Service::ParseSocket}},
        {"file",        {2,     2,    &Service::ParseFile}},
        {"user",        {1,     1,    &Service::ParseUser}},
        {"writepid",    {1,     kMax, &Service::ParseWritepid}},
    };

3.4.3 EndSection

EndSection 将创建并填充完成的 Sevice 对象加入到 services_ 链表中,首先会先依据service name在链表中查找是否已经有了,如果有的话需要先移除,再重新创建新的服务保存到链表中


Result<Success> ServiceParser::EndSection() {
    if (service_) {
        //先找到旧的service并移除
        Service* old_service = service_list_->FindService(service_->name());
        if (old_service) {
            if (!service_->is_override()) {
                return Error() << "ignored duplicate definition of service '" << service_->name()
                               << "'";
            }

            service_list_->RemoveService(*old_service);
            old_service = nullptr;
        }
        //添加service到链表中
        service_list_->AddService(std::move(service_));
    }

    return Success();
}

三、总结

总结来看,init.rc文件会依据不同的命令切换不同的Parser进行解析,解析过程中主要是执行ParseSection,ParseLineSection和EndSection来进行解析。此过程中会包含语法的检查,数据储存等。大致的过程如下:


Parser大致流程
上一篇下一篇

猜你喜欢

热点阅读