一蒙兰、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中主要有四個(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ò)程如下: