Android 8.0 系統啟動流程之init.rc解析與service流程(七)
1、概述
上一篇文章中我們介紹了一下init.rc檔案中的語法規則,而本文將分析如何解析rc檔案,並對rc檔案中的某一service啟動過程進行分析。
2、解析.rc檔案
之前我們在文件中看到.rc檔案主要有根目錄下的 /init.rc ,以及{system,vendor,odm}/etc/init/這三個目錄下的 *.rc ,
然後就是如果有一個特殊目錄被設定的話,就替代這些目錄,明白這些,下面的程式碼就好理解了.
int main(int argc, char** argv) {
...
const BuiltinFunctionMap function_map;
/*
* 1.C++中::表示靜態方法呼叫,相當於java中static的方法
*/
Action::set_function_map(&function_map); //將function_map存放到Action中作為成員屬性
Parser& parser = Parser::GetInstance();//單例模式,得到Parser物件
/*
* 1.C++中std::make_unique相當於new,它會返回一個std::unique_ptr,即智慧指標,可以自動管理記憶體
* 2.unique_ptr持有對物件的獨有權,兩個unique_ptr不能指向一個物件,不能進行復制操作只能進行移動操作
* 3.移動操作的函式是 p1=std::move(p) ,這樣指標p指向的物件就移動到p1上了
* 4.接下來的這三句程式碼都是new一個Parser(解析器),然後將它們放到一個map裡存起來
* 5.ServiceParser、ActionParser、ImportParser分別對應service action import的解析
*/
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty ()) {//如果ro.boot.init_rc沒有對應的值,則解析/init.rc以及/system/etc/init、/vendor/etc/init、/odm/etc/init這三個目錄下的.rc檔案
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {//如果ro.boot.init_rc屬性有值就解析屬性值
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}
...
}
2.1 ParseConfig
定義在 platform/system/core/init/init_parser.cpp
首先是判斷傳入的是目錄還是檔案,若是檔案則呼叫ParseConfigFile;若是目錄則遞迴呼叫ParseConfigDir遍歷該目錄下的所有檔案,對檔案進行呼叫ParseConfigFile.
bool Parser::ParseConfig(const std::string& path) {
if (is_dir(path.c_str())) {
return ParseConfigDir(path);//遍歷下該目錄中的檔案
}
return ParseConfigFile(path);
}
而ParseConfigFile就是讀取檔案中的資料後,將資料傳遞給ParseData函式,最後遍section_parsers呼叫其EndFile函式,EndFile後面再分析,因為是多型實現,我們先看ParseData
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
std::string data;
std::string err;
if (!ReadFile(path, &data, &err)) {//將資料讀取到data
LOG(ERROR) << err;
return false;
}
data.push_back('\n'); // TODO: fix parse_config.
ParseData(path, data);//解析資料
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
}
2.2 ParseData
ParseData 定義在 platform/system/core/init/init_parser.cpp
ParseData通過呼叫next_token函式遍歷每一個字元,以空格或”“為分割將一行拆分成若干個單詞,呼叫T_TEXT將單詞放到args陣列中,當讀到回車符就呼叫T_NEWLINE,在section_parsers_這個map中找到對應的on service import的解析器,執行ParseSection,如果在map中找不到對應的key,就執行ParseLineSection,當讀到0的時候,表示一個Section讀取結束,呼叫T_EOF執行EndSection.
void Parser::ParseData(const std::string& filename, const std::string& data) {
//TODO: Use a parser with const input and remove this copy
std::vector<char> data_copy(data.begin(), data.end()); //將data的內容複製到data_copy中
data_copy.push_back('\0'); //追加一個結束符0
parse_state state; //定義一個結構體
state.filename = filename.c_str();
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
std::vector<std::string> args;
for (;;) {
switch (next_token(&state)) { // 遍歷data_copy中每一個字元
case T_EOF: //如果是檔案結尾,則呼叫EndSection
if (section_parser) {
section_parser->EndSection();
}
return;
case T_NEWLINE://讀取了一行資料
state.line++;
if (args.empty()) {
break;
}
/*
* 1.section_parsers_是一個std:map
* 2.C++中std:map的count函式是查詢key,相當於Java中Map的contains
* 3.section_parsers_中只有三個key,on service import,之前AddSectionParser函式加入
*/
if (section_parsers_.count(args[0])) { //判斷是否包含 on service import
if (section_parser) {
section_parser->EndSection();
}
section_parser = section_parsers_[args[0]].get();//取出對應的parser
std::string ret_err;
if (!section_parser->ParseSection(args, &ret_err)) {//解析對應的Section
parse_error(&state, "%s\n", ret_err.c_str());
section_parser = nullptr;
}
} else if (section_parser) { //不包含 on service import則是command或option
std::string ret_err;
if (!section_parser->ParseLineSection(args, state.filename,
state.line, &ret_err)) {//解析command或option
parse_error(&state, "%s\n", ret_err.c_str());
}
}
args.clear();
break;
case T_TEXT: //將讀取的一行資料放到args中,args以空格或""作為分割,將一行資料拆分成單詞放進陣列中
args.emplace_back(state.text);
break;
}
}
}
這裡其實涉及到on service import對應的三個解析器ActionParser,ServiceParser,ImportParser,它們是在之前加入到section_parsers_這個map中的
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
void Parser::AddSectionParser(const std::string& name,
std::unique_ptr<SectionParser> parser) {
section_parsers_[name] = std::move(parser);
}
它們都是SectionParser的子類,SectionParser有四個純虛擬函式,分別是ParseSection、ParseLineSection、EndSection,EndFile.
class SectionParser {
public:
virtual ~SectionParser() {
}
/*
* 1.C++中純虛擬函式的定義格式是 virtual作為修飾符,然後賦值給0,相當於Java中的抽象方法
* 2.如果不賦值給0,卻以virtual作為修飾符,這種是虛擬函式,虛擬函式可以有方法體,相當於Java中父類的方法,主要用於子類的過載
* 3.只要包含純虛擬函式的類就是抽象類,不能new,只能通過子類實現,這個跟Java一樣
*/
virtual bool ParseSection(const std::vector<std::string>& args,
std::string* err) = 0;
virtual bool ParseLineSection(const std::vector<std::string>& args,
const std::string& filename, int line,
std::string* err) const = 0;
virtual void EndSection() = 0;
virtual void EndFile(const std::string& filename) = 0;
};
接下來我將分析這三個Perser的ParseSection、ParseLineSection、EndSection,EndFile具體實現
2.3 ActionParser
定義在platform/system/core/init/action.cpp
我們先看ParseSection,它先將args中下標1到結尾的資料複製到triggers陣列中,然後是構建Action物件,呼叫InitTriggers,解析這些trigger
bool ActionParser::ParseSection(const std::vector<std::string>& args,
std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end()); //將args複製到triggers中,除去下標0
if (triggers.size() < 1) {
*err = "actions must have a trigger";
return false;
}
auto action = std::make_unique<Action>(false);
if (!action->InitTriggers(triggers, err)) { //呼叫InitTriggers解析trigger
return false;
}
action_ = std::move(action);
return true;
}
InitTriggers通過比較是否以”property:”開頭,區分trigger的型別,如果是property trigger,就呼叫ParsePropertyTrigger,如果是event trigger,就將args的引數賦值給event_trigger_,型別是string
bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
const static std::string prop_str("property:");
for (std::size_t i = 0; i < args.size(); ++i) {
...
if (!args[i].compare(0, prop_str.length(), prop_str)) {
if (!ParsePropertyTrigger(args[i], err)) {
return false;
}
} else {
...
event_trigger_ = args[i];
}
}
return true;
}
ParsePropertyTrigger函式先是將字元以”=”分割為name-value,然後將name-value存入property_triggers_這個map中
bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length())); //擷取property:後的內容
size_t equal_pos = prop_name.find('=');
if (equal_pos == std::string::npos) {
*err = "property trigger found without matching '='";
return false;
}
std::string prop_value(prop_name.substr(equal_pos + 1)); //取出value
prop_name.erase(equal_pos); //刪除下標為equal_pos的字元,也就是刪除"="
if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
//將name-value存放到map中,emplace相當於put操作
*err = "multiple property triggers found for same property";
return false;
}
return true;
}
從上面看出,ParseSection函式的作用就是構造一個Action物件,將trigger條件記錄到Action這個物件中,如果是event trigger就賦值給event_trigger_,如果是property trigger就存放到property_triggers_這個map中. 接下來我們分析ParseLineSection。ParseLineSection是直接呼叫Action物件的AddCommand函式
bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
const std::string& filename, int line,
std::string* err) const {
return action_ ? action_->AddCommand(args, filename, line, err) : false;
}
AddCommand看名字就大概知道是新增命令,它首先是做一些引數空值的檢查,然後是呼叫FindFunction查詢命令對應的執行函式,最後將這些資訊包裝成Command物件存放到commands_陣列中,這裡比較關鍵的就是FindFunction
bool Action::AddCommand(const std::vector<std::string>& args,
const std::string& filename, int line, std::string* err) {
... //一些引數檢查
auto function = function_map_->FindFunction(args[0], args.size() - 1, err);//查詢命令對應的執行函式
if (!function) {
return false;
}
AddCommand(function, args, filename, line);
return true;
}
void Action::AddCommand(BuiltinFunction f,
const std::vector<std::string>& args,
const std::string& filename, int line) {
commands_.emplace_back(f, args, filename, line);//commands_是個陣列,emplace_back就相當於add
}
FindFunction定義在platform/system/core/init/keyword_map.h
這個函式主要作用是通過命令查詢對應的執行函式,比如.rc檔案中定義chmod,那我們得找到chmod具體去執行哪個函式. 它首先是通過map()返回一個std:map,呼叫其find函式,find相當於Java中的get,但是返回的是entry,可以通過entry ->first和entry ->second獲取key-value。找到的value是一個結構體,裡面有三個值,第一個是引數最小數目,第二個是引數最大數目,第三個就是執行函式,之後作了引數的數目檢查,也就是說命令後的引數要在最小值和最大值之間.
const Function FindFunction(const std::string& keyword,
size_t num_args,
std::string* err) const {
using android::base::StringPrintf;
auto function_info_it = map().find(keyword); //找到keyword對應的entry
if (function_info_it == map().end()) { // end是最後一個元素後的元素,表示找不到
*err = StringPrintf("invalid keyword '%s'", keyword.c_str());
return nullptr;
}
auto function_info = function_info_it->second;//獲取value
auto min_args = std::get<0>(function_info);//獲取引數數量最小值
auto max_args = std::get<1>(function_info);//獲取引數數量最大值
if (min_args == max_args && num_args != min_args) {//將實際引數數量與最大值最小值比較
*err = StringPrintf("%s requires %zu argument%s",
keyword.c_str(), min_args,
(min_args > 1 || min_args == 0) ? "s" : "");
return nullptr;
}
if (num_args < min_args || num_args > max_args) {
if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
*err = StringPrintf("%s requires at least %zu argument%s",
keyword.c_str(), min_args,
min_args > 1 ? "s" : "");
} else {
*err = StringPrintf("%s requires between %zu and %zu arguments",
keyword.c_str(), min_args, max_args);
}
return nullptr;
}
return std::get<Function>(function_info);//返回命令對應的執行函式
}
我們看看map()的實現,定義在platform/system/core/init/builtins.cpp
這個實現比較簡單,就是直接構造一個map,然後返回. 比如{“bootchart”, {1,1,do_bootchart}},表示命令名稱叫bootchart,對應的執行函式是do_bootchart,允許傳入的最小和最大引數數量是1
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); //表示size_t的最大值
// clang-format off
static const Map builtin_functions = {
{"bootchart", {1, 1, do_bootchart}},
{"chmod", {2, 2, do_chmod}},
{"chown", {2, 3, do_chown}},
{"class_reset", {1, 1, do_class_reset}},
{"class_restart", {1, 1, do_class_restart}},
{"class_start", {1, 1, do_class_start}},
{"class_stop", {1, 1, do_class_stop}},
{"copy", {2, 2, do_copy}},
{"domainname", {1, 1, do_domainname}},
{"enable", {1, 1, do_enable}},
{"exec", {1, kMax, do_exec}},
{"exec_start", {1, 1, do_exec_start}},
{"export", {2, 2, do_export}},
{"hostname", {1, 1, do_hostname}},
{"ifup", {1, 1, do_ifup}},
{"init_user0", {0, 0, do_init_user0}},
{"insmod", {1, kMax, do_insmod}},
{"installkey", {1, 1, do_installkey}},
{"load_persist_props", {0, 0, do_load_persist_props}},
{"load_system_props", {0, 0, do_load_system_props}},
{"loglevel", {1, 1, do_loglevel}},
{"mkdir", {1, 4, do_mkdir}},
{"mount_all", {1, kMax, do_mount_all}},
{"mount", {3, kMax, do_mount}},
{"umount", {1, 1, do_umount}},
{"restart", {1, 1, do_restart}},
{"restorecon", {1, kMax, do_restorecon}},
{"restorecon_recursive", {1, kMax, do_restorecon_recursive}},
{"rm", {1, 1, do_rm}},
{"rmdir", {1, 1, do_rmdir}},
{"setprop", {2, 2, do_setprop}},
{"setrlimit", {3, 3, do_setrlimit}},
{"start", {1, 1, do_start}},
{"stop", {1, 1, do_stop}},
{"swapon_all", {1, 1, do_swapon_all}},
{"symlink", {2, 2, do_symlink}},
{"sysclktz", {1, 1, do_sysclktz}},
{"trigger", {1, 1, do_trigger}},
{"verity_load_state", {0, 0, do_verity_load_state}},
{"verity_update_state", {0, 0, do_verity_update_state}},
{"wait", {1, 2, do_wait}},
{"wait_for_prop", {2, 2, do_wait_for_prop}},
{"write", {2, 2, do_write}},
};
// clang-format on
return builtin_functions;
}
接下來我們看看EndSection,直接是呼叫ActionManager::GetInstance().AddAction
void ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
ActionManager::GetInstance().AddAction(std::move(action_));
}
}
AddAction首先是查詢是否有存在的同名Action,如果有就將他們的命令合併,沒有就將它存入陣列actions_中
void ActionManager::AddAction(std::unique_ptr<Action> action) {
auto old_action_it =
std::find_if(actions_.begin(), actions_.end(),
[&action] (std::unique_ptr<Action>& a) {
return action->TriggersEqual(*a);
});//find_if是集合中用於比較的模板,上面這種寫法是lambda表示式
if (old_action_it != actions_.end()) {//在陣列actions中找到Action說明已經存在同名,就合併command
(*old_action_it)->CombineAction(*action);
} else { //找不到就加入陣列
actions_.emplace_back(std::move(action));
}
}
bool Action::TriggersEqual(const Action& other) const {
return property_triggers_ == other.property_triggers_ &&
event_trigger_ == other.event_trigger_;//比較之前記錄的event trigger和property trigger
}
void Action::CombineAction(const Action& action) {
for (const auto& c : action.commands_) { //將新的Action中的command合併到老的Action
commands_.emplace_back(c);
}
}
EndFile是一個空實現,定義在platform/system/core/init/action.h
class ActionParser : public SectionParser {
public:
ActionParser() : action_(nullptr) {
}
bool ParseSection(const std::vector<std::string>& args,
std::string* err) override;
bool ParseLineSection(const std::vector<std::string>& args,
const std::string& filename, int line,
std::string* err) const override;
void EndSection() override;
void EndFile(const std::string&) override { //空實現
}
private:
std::unique_ptr<Action> action_;
};
講了這麼多,小結一下ActionParser做的事情. 它有三個重要的過載函式,ParseSection、ParseLineSection、EndSection.
ParseSection函式的作用是構造一個Action物件,將trigger條件記錄到Action這個物件中ParseLineSection作用是根據命令在一個map中找到對應的執行函式,然後將資訊記錄到之前構造的Action中EndSection作用是將前兩步構造的Action存入一個數組中,存入之前比較下陣列中是否已經存在同名的Action,如果有就合併command
2.4 ServiceParser
定義在platform/system/core/init/service.cpp
我們還是分析它的四個函式ParseSection、ParseLineSection、EndSection、EndFile
ParseSection首先是判斷單詞個數至少有三個,因為必須有一個服務名稱和執行檔案,然後是判斷名稱是否合法,主要是一些長度及內容的檢查,最後就是構造一個Service物件
bool ServiceParser::ParseSection(const std::vector<std::string>& args,
std::string* err) {
if (args.size() < 3) { // 傳入單詞個數至少三個
*err = "services must have a name and a program";
return false;
}
const std::string& name = args[1];
if (!IsValidName(name)) {//檢查名稱是否合法
*err = StringPrintf("invalid service name '%s'", name.c_str());
return false;
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, str_args);// 構造Service物件
return true;
}
ParseLineSection直接執行Service的ParseLine函式
bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
const std::string& filename, int line,
std::string* err) const {
return service_ ? service_->ParseLine(args, err) : false;
}
ParseLine的思路跟之前Action一樣,就是根據option名稱從map中找到對應的執行函式,然後執行這個函式.這些執行函式主要作用就是對傳入引數做一些處理,然後將資訊記錄到Service物件中
bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
if (args.empty()) {
*err = "option needed, but not provided";
return false;
}
static const OptionParserMap parser_map;
auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);//從map中找出執行函式
if (!parser) {
return false;
}
return (this->*parser)(args, err);//執行找到的這個函式
}
map()返回的map如下,定義在定義在platform/system/core/init/service.cpp中
Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
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}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
{"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}},
{"oom_score_adjust",
{1, 1, &Service::ParseOomScoreAdjust}},
{"namespace", {1, 2, &Service::ParseNamespace}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
{"socket", {3, 6, &Service::ParseSocket}},
{"file", {2, 2, &Service::ParseFile}},
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
// clang-format on
return option_parsers;
}
接下來我們看看EndSection,直接呼叫ServiceManager的AddService函式
void ServiceParser::EndSection() {
if (service_) {
ServiceManager::GetInstance().AddService(std::move(service_));
}
}
AddService的實現比較簡單,就是通過比較service的name,檢視存放Service的陣列services_中是否有同名的service,如果有就列印下錯誤日誌,直接返回,
如果不存在就加入陣列中
void ServiceManager::AddService(std::unique_ptr<Service> service) {
Service* old_service = FindServiceByName(service->name()); //查詢services_中是否已存在同名service
if (old_service) {
LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
return;
}
services_.emplace_back(std::move(service));//加入陣列
}
Service* ServiceManager::FindServiceByName(const std::string& name) const {
auto svc = std::find_if(services_.begin(), services_.end(),
[&name] (const std::unique_ptr<Service>& s) {
return name == s->name();
});//跟之前action一樣,遍歷陣列進行比較,查詢同名service
if (svc != services_.end()) {
return svc->get(); //找到就返回service
}
return nullptr;
}
EndFile依然是一個空實現,定義在platform/system/core/init/service.h
class ServiceParser : public SectionParser {
public:
ServiceParser() : service_(nullptr) {
}
bool ParseSection(const std::vector<std::string>& args,
std::string* err) override;
bool ParseLineSection(const std::vector<std::string>& args,
const std::string& filename, int line,
std::string* err) const override;
void EndSection() override;
void EndFile(const std::string&) override { //空實現
}
private:
bool IsValidName(const std::string& name) const;
std::unique_ptr<Service> service_;
};
從上面可以看出,ServiceParser的處理跟ActionParser差不多,區別在於Action將執行函式存起來等待Trigger觸發時執行,Service找到執行函式後是馬上執行
2.4 ImportParser
定義在platform/system/core/init/import_parser.cpp
最後我們看看ImportParser,ImportParser的ParseLineSection、EndSection都是空實現,只實現了ParseSection和EndFile,因為它的語法比較單一,只有一行. 我們來看看它的ParseSection函式
首先檢查單詞只能是兩個,因為只能是import xxx 這種語法,然後呼叫expand_props處理下引數,最後將結果放入陣列imports_存起來
bool ImportParser::ParseSection(const std::vector<std::string>& args,
std::string* err) {
if (args.size() != 2) { //檢查引數只能是兩個
*err = "single argument needed for import\n";
return false;
}
std::string conf_file;
bool ret = expand_props(args[1], &conf_file); //處理第二個引數
if (!ret) {
*err = "error while expanding import";
return false;
}
LOG(INFO) << "Added '" << conf_file << "' to import list";
imports_.emplace_back(std::move(conf_file)); //存入陣列
return true;
}
expand_props 定義在platform/system/core/init/util.cpp ,主要作用就是找到x.y這種語法,將x.y取出來作為name,去屬性系統中找對應的value,然後替換
bool expand_props(const std::string& src, std::string* dst) {
const char* src_ptr = src.c_str();
if (!dst) {