Android啟動(dòng)流程之二 init.rc解析

一蒙兰、init.rc

Android中利用rc文件配置一些需要提前執(zhí)行的操作镜沽,在系統(tǒng)啟動(dòng)的時(shí)候解析并執(zhí)行炊昆,為啟動(dòng)Android系統(tǒng)核心服務(wù)提供保障桨吊。可參考:http://androidxref.com/9.0.0_r3/xref/system/core/init/README.md

rc文件以行為單位凤巨,一行定義一個(gè)語(yǔ)句视乐,使用#作為注釋。rc語(yǔ)法核心包括

  • Action
  • Service
  • Command
  • Options

其中需要注意的是Action和Service的名稱是唯一的敢茁。
rc文件是可以通過(guò)import語(yǔ)句來(lái)導(dǎo)入其他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開頭彰檬,通過(guò)觸發(fā)器trigger來(lái)決定對(duì)應(yīng)的service什么時(shí)候執(zhí)行伸刃,執(zhí)行的時(shí)機(jī)有

  • on early-init -> 初始化的早期觸發(fā)
  • on init -> 初始化階段觸發(fā)
  • on late-init -> 初始化后期觸發(fā)
  • on boot/charger -> 系統(tǒng)啟動(dòng)或者充電時(shí)觸發(fā)
  • on property:<key>=<value> -> 滿足條件時(shí)觸發(fā)
    定義語(yǔ)法
on <trigger> #觸發(fā)條件
    <command> #觸發(fā)命令
    <command1>#第二個(gè)觸發(fā)命令,可以執(zhí)行多個(gè)命令

1.2逢倍、Service

Service捧颅,顧名思義就是需要啟動(dòng)的服務(wù),以service開頭较雕,init啟動(dòng)后會(huì)由init進(jìn)程啟動(dòng)它作為子進(jìn)程碉哑。
定義:

service <name> <pathname> [<argument>]*  #可以有多個(gè)參數(shù)
    <option>
    <option> #可以有多個(gè)option

例子

service servicemanager /system/bin/servicemanager

定義的就是名稱為servicemanager的服務(wù),對(duì)應(yīng)的執(zhí)行文件為system/bin/servicemanager亮蒋,因此在啟動(dòng)服務(wù)前需要判斷服務(wù)對(duì)應(yīng)的文件是否存在

1.3扣典、Command

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

  • class_start<service_class_name> -> 啟動(dòng)屬于同一個(gè)class的所有服務(wù)
  • start<service_name> ->啟動(dòng)指定的服務(wù)
  • stop <service_name> ->停止正在運(yùn)行的服務(wù)
  • setprop<name> <value> -> 設(shè)置系統(tǒng)屬性值
  • symlink <target><sym_link> ->創(chuàng)建連接到target的sym_link符號(hào)連接
  • write<path><string> -> 向path路徑下寫入字符串
  • exec -> fork 并執(zhí)行宛蚓,init進(jìn)程會(huì)被阻塞激捏,知道執(zhí)行完畢
  • export -> 設(shè)置環(huán)境變量

1.4、Options

Option是Service配合使用的可選操作項(xiàng)

  • disable -> 不跟隨class自動(dòng)啟動(dòng)凄吏,只有跟隨service name才啟動(dòng)
  • oneshot -> service執(zhí)行完畢退出后不會(huì)再重啟
  • user/group -> 設(shè)置service的用戶和群組远舅,默認(rèn)是root
  • class -> 設(shè)置所屬的類名闰蛔,service綁定的class啟動(dòng)或者退出時(shí),service也會(huì)啟動(dòng)或者退出图柏,默認(rèn)是default
  • onrestart ->服務(wù)重啟時(shí)執(zhí)行
  • socket -> 創(chuàng)建名為/dev/socket/name的socket
  • critical -> 在規(guī)定的時(shí)間內(nèi)service如果不斷重啟序六,系統(tǒng)會(huì)重啟進(jìn)入recovery模式

1.5 demo

#初始化早期執(zhí)行
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
    #啟動(dòng)ueventd服務(wù)
    start ueventd

#初始化時(shí)執(zhí)行
on init
  mkdir /dev/stune
  write /proc/sys/kernel/sched_tunable_scaling 0

#當(dāng)sys.boot_from_charger_mode為1時(shí)執(zhí)行
on property:sys.boot_from_charger_mode=1
    class_stop charger
    trigger late-init

#初始化后期執(zhí)行
on late-init
    trigger early-fs

# 定義一個(gè)名為flash_recovery,執(zhí)行文件在/system/bin/install-recovery.sh的服務(wù)
# 該服務(wù)是oneshot的,執(zhí)行完成后不再重啟蚤吹,且啟動(dòng)或者退出動(dòng)作和main服務(wù)一起
service flash_recovery /system/bin/install-recovery.sh
    class main
    oneshot

二例诀、init.rc解析

《Android啟動(dòng)流程之一 init進(jìn)程啟動(dòng)》中有提到,init進(jìn)程的main函數(shù)中有加載init.rc文件裁着,對(duì)應(yīng)的執(zhí)行函數(shù)為:

2.1繁涂、LoadBootScripts

先判斷是否自定義了rc文件,如果沒有的話就讀取默認(rèn)的/init.rc

system/core/init/init.cpp

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    //創(chuàng)建解析器
    Parser parser = CreateParser(action_manager, service_list);
    //先判斷是否自定義了rc文件二驰,如果沒有的話就讀取默認(rèn)的/init.rc
    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        //解析/init.rc
        parser.ParseConfig("/init.rc");
        //以下這些rc文件如果第一次解析失敗扔罪,就放到完一點(diǎn)的解析隊(duì)列中
        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);
    }
}

這個(gè)過(guò)程中最主要的一個(gè)操作是,調(diào)用CreateParser方法創(chuàng)建了rc文件的解析器桶雀,有三種解析器矿酵,分別是:

  • service解析器,用于解析service開頭的rc語(yǔ)句矗积,對(duì)應(yīng)的類是service.cpp
  • on 解析器全肮,用于解析on 開頭的語(yǔ)句.對(duì)應(yīng)的類是action_parser.cpp
  • import解析器,用于解析import開頭的語(yǔ)句棘捣,該語(yǔ)句定義了讓rc文件之間可以互相包含進(jìn)來(lái).對(duì)應(yīng)的類是import_parser.cpp

類圖為:


Parser解析器類圖

Parser中主要有四個(gè)關(guān)鍵方法:

  • ParseSection 該方法主要解析主要的指令辜腺,例如service,on,import等
  • ParseLineSection 該方法主要是解析指令service乍恐,on后面跟隨的Command和options
  • EndSection 解析每個(gè)命令配置完成后調(diào)用哪自,這時(shí)解析完成的數(shù)據(jù)需要在這里進(jìn)行保存
  • EndFile 解析完成一個(gè)文件時(shí)調(diào)用

2.2、ParseConfig & ParseData方法

ParseConfig會(huì)調(diào)用兩個(gè)參數(shù)的ParseConfig禁熏,這里會(huì)判斷傳入的path是文件還是目錄,如果是文件的話調(diào)用ParseConfigFile解析邑彪,如果是目錄的話調(diào)用ParseConfigDir遍歷目錄下的rc文件全部解析瞧毙,這個(gè)步驟可以忽略不用太關(guān)注,最終都會(huì)調(diào)用到ParseData方法真正解析文件寄症,整個(gè)解析過(guò)程會(huì)掃描配置文件宙彪,解析指令存放到鏈表中,在init進(jìn)程中執(zhí)行有巧。
ParseData函數(shù)

system/core/init/parser.cpp

//第一個(gè)參數(shù):文件路徑释漆,第二個(gè)參數(shù):文件內(nèi)容,第三個(gè)參數(shù):錯(cuò)誤信息
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
    //開始之前為了安全考慮篮迎,先復(fù)制一份文件內(nèi)容進(jìn)行解析
    std::vector<char> data_copy(data.begin(), data.end());
    //在讀取到的數(shù)據(jù)最后添加一個(gè)‘\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;
        //調(diào)用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: //字符串解析結(jié)束
                end_section();
                return;
            case T_NEWLINE://解析完一行男图,新開始一行的解析
                //開始讀取新的一行示姿,line加1
                state.line++;
                //如果始終沒有讀取到文本則進(jìn)入下一個(gè)循環(huán),讀取下一行
                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.
                //這里主要是針對(duì)ueventd.rc文件的處理逊笆,識(shí)別 /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三者之一
                    //依據(jù)這個(gè)來(lái)匹配到對(duì)應(yīng)的Parser
                    section_parser = section_parsers_[args[0]].get();
                    section_start_line = state.line;
                    //解析Action關(guān)鍵字行
                    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對(duì)應(yīng)的命令行
                    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是一個(gè)結(jié)構(gòu)體
                //struct parse_state
                //{
                //  char *ptr;      // 要解析的字符串
                //  char *text;     // 解析到的字符串难裆,可以理解為返回一行的數(shù)據(jù)
                //  int line;       // 解析到第行數(shù)
                //  int nexttoken;  // 解析狀態(tài)子檀,有 T_EOF、T_NEWLINE乃戈、T_TEXT 三種
                //};
                //
                //
                args.emplace_back(state.text);
                break;
        }
    }
}

ParseData函數(shù)中我們需要注意以下幾點(diǎn)

  • arg[0] 是每一行讀取到的第一個(gè)字符串褂痰,解析過(guò)程中會(huì)依據(jù)這個(gè)字符串來(lái)動(dòng)態(tài)切換解析器,字符串和解析器的對(duì)應(yīng)關(guān)系為:service -> ServiceParser, on -> ActionParser,import -> ImportParser
  • parse_state 是一個(gè)結(jié)構(gòu)體
struct parse_state{
   char *ptr; //要解析的字符串
   char *text;//解析到的字符串症虑,一般為一行字符串
   int line; //解析到第幾行
   int nexttoken; //解析狀態(tài)缩歪,有 T_EOF、T_NEWLINE侦讨、T_TEXT 三種
}

  • nexttoken,解析狀態(tài)驶冒,T_EOF 表示字符串解析結(jié)束,T_NEWLINE 表示解析完一行的數(shù)據(jù)韵卤,T_TEXT 表示解析到一個(gè)單詞

2.3骗污、ActionParser

上面有提供Parser的類圖大致結(jié)構(gòu),在解析過(guò)程中實(shí)際的解析會(huì)在對(duì)應(yīng)的Parser中進(jìn)行沈条。ActionParser主要是解析以 on開頭的一系列命令需忿,Action決定對(duì)應(yīng)的動(dòng)作什么時(shí)候會(huì)執(zhí)行,解析完成后Action會(huì)被添加到存放Action的隊(duì)列中蜡歹。Action的每個(gè)命令是按順序排隊(duì)執(zhí)行屋厘,每個(gè)命令依次進(jìn)入執(zhí)行狀態(tài)。
ActionParser實(shí)際上就是把配置解析為Action結(jié)構(gòu)月而,對(duì)應(yīng)的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是否有定義對(duì)應(yīng)的trigger觸發(fā)器,如果沒有的話就返回失敗
    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觸發(fā)器,提取event_trigger和property_triggers
    if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
        !result) {
        return Error() << "ParseTriggers() failed: " << result.error();
    }
    //創(chuàng)建Action對(duì)象
    auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
                                           property_triggers);

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

這里傳遞給ParseTriggers方法的triggers是去除了命令關(guān)鍵字on之外的觸發(fā)器信息父款,ParseTriggers會(huì)判斷是否是屬性變化觸發(fā)的觸發(fā)器溢谤,如果是屬性變化的觸發(fā)器的話填充到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觸發(fā)器需要通過(guò) && 連接
        //例如: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中滿足條件時(shí)會(huì)被執(zhí)行,這里主要是屬性變化觸發(fā)器的解析
        //這里會(huì)把屬性變化的觸發(fā)條件填充到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";
            }
            //如果不是屬性變化的觸發(fā)器肝集,則填充到event_trigger中
            *event_trigger = args[i];
        }
    }

    return Success();
}

ParseTriggers做了兩件事情

  • 觸發(fā)器語(yǔ)法判斷 && 連接符
  • 觸發(fā)器依據(jù)是否是屬性變化區(qū)分不同觸發(fā)器提取到不同的數(shù)據(jù)結(jié)構(gòu)里面

2.3.2瞻坝、ParseLineSection

解析完成命令關(guān)鍵字部分后就會(huì)解析對(duì)應(yīng)的Command子模塊,ParseLineSection直接調(diào)用了Action的AddCommand函數(shù)


Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
    if (!function_map_) {
        return Error() << "no function map available";
    }
    //通過(guò)args調(diào)用FindFunction查找對(duì)應(yīng)的處理命令杏瞻,類似解析命令關(guān)鍵字行的做法
    //利用args[0]進(jìn)行匹配所刀,function_map在init進(jìn)程啟動(dòng)的時(shí)候已經(jīng)初始化了衙荐。
    auto function = function_map_->FindFunction(args);
    if (!function) return Error() << function.error();

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

FindFunction函數(shù)在keyword_map.h中會(huì)通過(guò)args[0]以及具體的字符內(nèi)容匹配到最終的命令參數(shù),最后填充到commands_中
function_map_的內(nèi)容如下(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對(duì)應(yīng)的各字段勉痴,從左到右

  • 函數(shù)名
  • 最小參數(shù)個(gè)數(shù)
  • 最大參數(shù)個(gè)數(shù)
  • 處理的函數(shù)地址

2.3.3赫模、EndSection

這里主要是將最終解析出來(lái)的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結(jié)構(gòu)比較復(fù)雜蒸矛,感興趣的話可以閱讀system/core/init/service.h文件瀑罗。但是主要還是定義了Service的一些操作函數(shù),rc文件中定義的一些命令和Options

2.4.1雏掠、ParseSection

Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
                                            const std::string& filename, int line) {
    //首先需要驗(yàn)證service配置是否正確斩祭,service配置必須包含
    //服務(wù)名和啟動(dòng)參數(shù)
    if (args.size() < 3) {
        return Error() << "services must have a name and a program";
    }

    const std::string& name = args[1];
    //驗(yàn)證服務(wù)名是否合法
    if (!IsValidName(name)) {
        return Error() << "invalid service name '" << name << "'";
    }

   ......
    //服務(wù)啟動(dòng)參數(shù)
    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命令快接下來(lái)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是固定的摧玫,內(nèi)容如下,參數(shù)分別代表:處理關(guān)鍵字、最小參數(shù)個(gè)數(shù)绑青、最大參數(shù)個(gè)數(shù)诬像、處理函數(shù)地址。:

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 將創(chuàng)建并填充完成的 Sevice 對(duì)象加入到 services_ 鏈表中,首先會(huì)先依據(jù)service name在鏈表中查找是否已經(jīng)有了闸婴,如果有的話需要先移除坏挠,再重新創(chuàng)建新的服務(wù)保存到鏈表中


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

三、總結(jié)

總結(jié)來(lái)看邪乍,init.rc文件會(huì)依據(jù)不同的命令切換不同的Parser進(jìn)行解析降狠,解析過(guò)程中主要是執(zhí)行ParseSection,ParseLineSection和EndSection來(lái)進(jìn)行解析庇楞。此過(guò)程中會(huì)包含語(yǔ)法的檢查榜配,數(shù)據(jù)儲(chǔ)存等。大致的過(guò)程如下:


Parser大致流程
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吕晌,一起剝皮案震驚了整個(gè)濱河市蛋褥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌睛驳,老刑警劉巖壁拉,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柏靶,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)溃论,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門屎蜓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人钥勋,你說(shuō)我怎么就攤上這事炬转×咎Γ” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵扼劈,是天一觀的道長(zhǎng)驻啤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)荐吵,這世上最難降的妖魔是什么骑冗? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮先煎,結(jié)果婚禮上贼涩,老公的妹妹穿的比我還像新娘。我一直安慰自己薯蝎,他們只是感情好遥倦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著占锯,像睡著了一般袒哥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上消略,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天堡称,我揣著相機(jī)與錄音,去河邊找鬼疑俭。 笑死粮呢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钞艇。 我是一名探鬼主播啄寡,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哩照!你這毒婦竟也來(lái)了挺物?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤飘弧,失蹤者是張志新(化名)和其女友劉穎识藤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體次伶,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡痴昧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冠王。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赶撰。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出豪娜,到底是詐尸還是另有隱情餐胀,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布瘤载,位于F島的核電站否灾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鸣奔。R本人自食惡果不足惜墨技,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望溃蔫。 院中可真熱鬧健提,春花似錦、人聲如沸伟叛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)统刮。三九已至紊遵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侥蒙,已是汗流浹背暗膜。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鞭衩,地道東北人学搜。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像论衍,于是被迫代替她去往敵國(guó)和親瑞佩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容