Android啟動流程之二 init.rc解析

一、init.rc

Android中利用rc文件配置一些需要提前執(zhí)行的操作,在系統(tǒng)啟動的時候解析并執(zhí)行,為啟動Android系統(tǒng)核心服務(wù)提供保障??蓞⒖迹?a target="_blank">http://androidxref.com/9.0.0_r3/xref/system/core/init/README.md

rc文件以行為單位,一行定義一個語句,使用#作為注釋。rc語法核心包括

  • Action
  • Service
  • Command
  • Options

其中需要注意的是Action和Service的名稱是唯一的。
rc文件是可以通過import語句來導(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開頭,通過觸發(fā)器trigger來決定對應(yīng)的service什么時候執(zhí)行,執(zhí)行的時機(jī)有

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

1.2、Service

Service,顧名思義就是需要啟動的服務(wù),以service開頭,init啟動后會由init進(jìn)程啟動它作為子進(jìn)程。
定義:

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

例子

service servicemanager /system/bin/servicemanager

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

1.3、Command

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

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

1.4、Options

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

  • disable -> 不跟隨class自動啟動,只有跟隨service name才啟動
  • oneshot -> service執(zhí)行完畢退出后不會再重啟
  • user/group -> 設(shè)置service的用戶和群組,默認(rèn)是root
  • class -> 設(shè)置所屬的類名,service綁定的class啟動或者退出時,service也會啟動或者退出,默認(rèn)是default
  • onrestart ->服務(wù)重啟時執(zhí)行
  • socket -> 創(chuàng)建名為/dev/socket/name的socket
  • critical -> 在規(guī)定的時間內(nèi)service如果不斷重啟,系統(tǒng)會重啟進(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
    #啟動ueventd服務(wù)
    start ueventd

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

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

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

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

二、init.rc解析

《Android啟動流程之一 init進(jìn)程啟動》中有提到,init進(jìn)程的main函數(shù)中有加載init.rc文件,對應(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);
    }
}

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

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

類圖為:


Parser解析器類圖

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

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

2.2、ParseConfig & ParseData方法

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

system/core/init/parser.cpp

//第一個參數(shù):文件路徑,第二個參數(shù):文件內(nèi)容,第三個參數(shù):錯誤信息
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ù)最后添加一個‘\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)入下一個循環(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.
                //這里主要是針對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三者之一
                    //依據(jù)這個來匹配到對應(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對應(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是一個結(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] 是每一行讀取到的第一個字符串,解析過程中會依據(jù)這個字符串來動態(tài)切換解析器,字符串和解析器的對應(yīng)關(guān)系為:service -> ServiceParser, on -> ActionParser,import -> ImportParser
  • parse_state 是一個結(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 表示解析到一個單詞

2.3、ActionParser

上面有提供Parser的類圖大致結(jié)構(gòu),在解析過程中實(shí)際的解析會在對應(yīng)的Parser中進(jìn)行。ActionParser主要是解析以 on開頭的一系列命令,Action決定對應(yīng)的動作什么時候會執(zhí)行,解析完成后Action會被添加到存放Action的隊(duì)列中。Action的每個命令是按順序排隊(duì)執(zhí)行,每個命令依次進(jìn)入執(zhí)行狀態(tài)。
ActionParser實(shí)際上就是把配置解析為Action結(jié)構(gòu),對應(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是否有定義對應(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對象
    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會判斷是否是屬性變化觸發(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ā)器需要通過 && 連接
        //例如: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中滿足條件時會被執(zhí)行,這里主要是屬性變化觸發(fā)器的解析
        //這里會把屬性變化的觸發(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ā)器語法判斷 && 連接符
  • 觸發(fā)器依據(jù)是否是屬性變化區(qū)分不同觸發(fā)器提取到不同的數(shù)據(jù)結(jié)構(gòu)里面

2.3.2、ParseLineSection

解析完成命令關(guān)鍵字部分后就會解析對應(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";
    }
    //通過args調(diào)用FindFunction查找對應(yīng)的處理命令,類似解析命令關(guān)鍵字行的做法
    //利用args[0]進(jìn)行匹配,function_map在init進(jìn)程啟動的時候已經(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中會通過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對應(yīng)的各字段,從左到右

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

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結(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ù)名和啟動參數(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ù)啟動參數(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命令快接下來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ù)個數(shù)、最大參數(shù)個數(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 對象加入到 services_ 鏈表中,首先會先依據(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é)來看,init.rc文件會依據(jù)不同的命令切換不同的Parser進(jìn)行解析,解析過程中主要是執(zhí)行ParseSection,ParseLineSection和EndSection來進(jìn)行解析。此過程中會包含語法的檢查,數(shù)據(jù)儲存等。大致的過程如下:


Parser大致流程
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容