1. 概述
?? 建议如果对init.rc文件中的语法规则存在疑问的话,先观看上一篇文章源码详解Android 9.0§ 系统启动流程之init.rc语法规则简单了解一下init.rc的语法规则,而本文将分析如何解析rc文件,并对rc文件中的某一service启动过程进行分析。
2. init进程解析init.rc过程分析
?? 前一篇文章我们恶补了相关的rc语法知识,前面这些都是为了init进程解析init.rc铺垫的。 ?? 之前我们在文档中看到.rc文件主要有根目录下的 /init.rc ,以及{system,vendor,odm}/etc/init/这三个目录下的 *.rc , 然后就是如果有一个特殊目录被设置的话,就替代这些目录,明白这些,下面的代码就好理解了。 由于LoadBootScripts和CreateParser函数都定义于system/core/init/init.cpp中,因此合并展示。
int main(int argc, char** argv) {
...
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
subcontexts = InitializeSubcontexts();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);
......
}
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.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 {
parser.ParseConfig(bootscript);
}
}
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
}
2.1 Parser
?? 可以看出在正式解析前,创建了一个Parser 对象(该类定义在system/core/init/parser.h中):
Parser parser = CreateParser(action_manager, service_list);
2.1.1 CreateParser
?? 全部代码在main函数一起展示,我们重点分析其中的代码:
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
?? 这段代码比较容易理解,分别初始化ServiceParser用来解析"service"块,初始化ActionParser用来解析"on"块,初始化ImportParser用来解析“import”块。
2.2 ParseConfig
?? 下面就要开始分析解析过程了,ParseConfig的代码定义在system/core/init/parser.cpp中。 ?? 首先是判断传入的是目录还是文件,若是文件则调用ParseConfigFile;若是目录则递归调用ParseConfigDir遍历该目录下的所有文件,对文件进行调用ParseConfigFile。
bool Parser::ParseConfig(const std::string& path) {
size_t parse_errors;
return ParseConfig(path, &parse_errors);
}
bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
*parse_errors = 0;
if (is_dir(path.c_str())) {
return ParseConfigDir(path, parse_errors);
}
return ParseConfigFile(path, parse_errors);
}
2.2.1 ParseConfigDir
??按照顺序,我们先来分析ParseConfigDir函数的内容: ??ParseConfigDir和ParseConfigFile函数同样定义于system/core/init/parser.cpp中。
bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir) {
PLOG(ERROR) << "Could not import directory '" << path << "'";
return false;
}
dirent* current_file;
std::vector<std::string> files;
while ((current_file = readdir(config_dir.get()))) {
if (current_file->d_type == DT_REG) {
std::string current_path =
android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
files.emplace_back(current_path);
}
}
std::sort(files.begin(), files.end());
for (const auto& file : files) {
if (!ParseConfigFile(file, parse_errors)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
return true;
}
2.2.2 ParseConfigFile
??刚刚在ParseConfigDir函数中,我们不难发现重点依旧是ParseConfigFile,让我们重点分析一下该函数:
bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
auto config_contents = ReadFile(path);
if (!config_contents) {
LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
return false;
}
config_contents->push_back('\n');
ParseData(path, *config_contents, parse_errors);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
}
??通过解析代码可以看到ParseConfigFile的逻辑比较简单,就是读取文件的内容为字符串,然后调用ParseData进行解析。
2.3 ParseData
??在正式开始分析代码前,先来一个流程图,防止大伙在代码分析中迷失了自己,可以回过头找到自己在哪里。
??ParseData函数定义在system/core/init/parser.cpp中,负责根据关键字解析出服务和动作块。
void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
parse_state state;
state.line = 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;
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:
state.line++;
if (args.empty()) break;
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])) {
end_section();
section_parser = section_parsers_[args[0]].get();
section_start_line = state.line;
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) {
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.emplace_back(state.text);
break;
}
}
}
通过上面的代码我们可以看到,ParseData主要通过调用next_token函数遍历每一个字符,然后对不同的字符进行判断,采取不同的规则进行处理,其主要流程如下:
- 以空格或""为分割将一行拆分成若干个单词,调用T_TEXT将单词放到args数组中
- 当读到回车符就调用T_NEWLINE,在section_parsers_这个map中找到对应的on service import的解析器,执行ParseSection
- 如果在map中找不到对应的key,就执行ParseLineSection,当读到0的时候,表示一个Section读取结束,调用T_EOF执行EndSection.
在上述流程中牵涉到一个非常重要的结构体parse_state ,next_token 处理的数据以 parse_state 结构体指针返回,以行为单位分隔传递的字符串,成员变量代表的意义如下:
#define T_EOF 0
#define T_TEXT 1
#define T_NEWLINE 2
struct parse_state
{
char *ptr;
char *text;
int line;
int nexttoken;
};
??其中 T_EOF 表示字符串解析结束,T_NEWLINE 表示解析完一行的数据,T_TEXT 表示解析到一个单词,在代码中会填充到 args vector 向量中,用作后续的解析处理。
??这里其实涉及到on service import对应的三个解析器ActionParser、ServiceParser、ImportParser,它们是在之前加入到section_parsers_这个map中的,大家应该有印象,代码如下:
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
}
??上述三个类都是SectionParser的子类,SectionParser有四个纯虚函数,分别是ParseSection、ParseLineSection、EndSection、EndFile
class SectionParser {
public:
virtual ~SectionParser() {}
virtual Result<Success> ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) = 0;
virtual Result<Success> ParseLineSection(std::vector<std::string>&&, int) { return Success(); };
virtual Result<Success> EndSection() { return Success(); };
virtual void EndFile(){};
};
??经过上面的这些步骤init.rc就彻底被解决为一个个的setcion,而每个Action或者Service则会别对应的SectionParser 来进一步处理,Service会别ServiceParser解析,而Action则会被ActionParser解析,Import则会被ImportParser解析。最后ParseSection 和 ParseLineSection 都是解析 args 参数填充一些链表(service_list_、_commands、_trigger等)记录所要配置或者执行触发的信息,在 init 程序的最后 while 循环中分别进行相应的配置操作。
2.4 ServiceParser
??该函数定义在system/core/init/service.h实现在system/core/init/service.cpp中,ServiceParser实现了对Service section的解析,下面让我们来分析其几个主要函数ParseSection,ParseLineSection和EndSection的实现。
2.4.1 ServiceParser::ParseSection
Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
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 << "'";
}
Subcontext* restart_action_subcontext = nullptr;
if (subcontexts_) {
for (auto& subcontext : *subcontexts_) {
if (StartsWith(filename, subcontext.path_prefix())) {
restart_action_subcontext = &subcontext;
break;
}
}
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
return Success();
}
ParseSection的函数处理逻辑如下:
- 首先判断传入进来的参数args单词个数是否至少有三个,因为至少要有一个服务名称和路径
- 然后判断名称是否合法,主要是检测长度和内容
- 经过上面的检测以后,就构造一个Service对象出来了
2.4.2 ServiceParser::ParseLineSection
??前面通过ParseSection定位到了Service section,接着调用ParseLineSection解析Service section中的options选项,即类似的如下的options:
class core
console
disabled
user shell
group shell log readproc
seclabel u:r:shell:s0
setenv HOSTNAME console
Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
return service_ ? service_->ParseLine(std::move(args)) : Success();
}
??ParseLine的思路就是根据option名称从map中找到对应的执行函数,然后执行这个函数。这些执行函数主要作用就是对传入参数做一些处理,然后将信息记录到Service对象中
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);
}
??ParseLineSection直接执行Service的ParseLine函数,然后是调用FindFunction查找命令对应的执行函数,这里比较关键的函数就是FindFunction了,让我们先来分析一下这个函数,你会发现在OptionParserMap 中找不到这个函数,那就只能是定义在其父类KeywordMap中查找了。
??FindFunction函数是定在system/core/init/keyword_map.h中,为KeywordMap类的方法
class KeywordMap {
public:
using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
using Map = std::map<std::string, FunctionInfo>;
virtual ~KeywordMap() {
}
const Result<Function> FindFunction(const std::vector<std::string>& args) const {
using android::base::StringPrintf;
if (args.empty()) return Error() << "Keyword needed, but not provided";
auto& keyword = args[0];
auto num_args = args.size() - 1;
auto function_info_it = map().find(keyword);
if (function_info_it == map().end()) {
return Error() << StringPrintf("Invalid keyword '%s'", keyword.c_str());
}
auto function_info = function_info_it->second;
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) {
return Error() << StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args,
(min_args > 1 || min_args == 0) ? "s" : "");
}
if (num_args < min_args || num_args > max_args) {
if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
return Error() << StringPrintf("%s requires at least %zu argument%s",
keyword.c_str(), min_args, min_args > 1 ? "s" : "");
} else {
return Error() << StringPrintf("%s requires between %zu and %zu arguments",
keyword.c_str(), min_args, max_args);
}
}
return std::get<Function>(function_info);
}
private:
virtual const Map& map() const = 0;
};
通过对上述代码分析我们可以知道,这个函数主要作用是通过命令查找对应的执行函数,譬如Service section中的option选项class,我们得找到ParseClass去执行那个函数。它的具体执行步骤如下:
- 它首先是通过map()返回一个std:map
- 接着调用其find函数,find相当于Java中的get,但是返回的是entry,可以通过entry ->first和entry ->second获取key-value.找到的value是一个结构体,里面有三个值,第一个是参数最小数目,第二个是参数最大数目,第三个就是执行函数,
- 然后做些参数相关的检查,然后返回查找到的对应的处理函数
这里解析Service section的option的map()函数的实现在system/core/init/service.cpp中,就是直接构造一个map,然后返回,譬如{“class”, {1, kMax, &Service::ParseClass}},这里表示命令名称叫做class,对应的执行函数是ParseClass,允许传入的最小是1,最大的是kMax。
class Service::OptionParserMap : public KeywordMap<OptionParser> {
public:
OptionParserMap() {}
private:
const Map& map() const override;
};
const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
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}},
};
return option_parsers;
}
??好了ParseLineSection就分析完了,下面我们对其总结一下,ParseLineSection主要是调用Service的ParseLine函数,然后根据传入的option名称从map中查找对应的执行函数,然后执行这个函数,这些函数的主要作用就是对传入的option参数做处理,然后将信息记录到Service对象中。
2.4.3 ServiceParser::EndSection
??通过前面的操作我们经过一系列猛虎般的操作,已经解析完了Service section了并且创建了Service对象了,那么我们怎么将其加入到init进程的ServiceList中呢,这里就得EndSection出马了。
Result<Success> ServiceParser::EndSection() {
if (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_list_->AddService(std::move(service_));
}
return Success();
}
??接下来看一下其中的FindService和AddService函数
??FindService
??代码定义在system\core\init\service.h中。
template <typename T, typename F = decltype(&Service::name)>
Service* FindService(T value, F function = &Service::name) const {
auto svc = std::find_if(services_.begin(), services_.end(),
[&function, &value](const std::unique_ptr<Service>& s) {
return std::invoke(function, s) == value;
});
if (svc != services_.end()) {
return svc->get();
}
return nullptr;
}
??AddService
??代码定义在system\core\init\service.cpp中。
void ServiceList::AddService(std::unique_ptr<Service> service) {
services_.emplace_back(std::move(service));
}
EndSection的处理逻辑简单,主要做了如下几个操作:
- 通过FindService查找是否已经有相同名字的Service存在
- 调用AddService将Service添加到services_中,这个services_是属于init进程中的。是从init.cpp一路传过来的,这个千万要注意。
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);
2.4.4 ServiceParser::EndFile
??如果说前面的函数轰轰烈烈,那么EndFile就平淡无奇了,EndFile是一个空函数没有做任何事情。
2.5 ActionParser
??在 2.4章节中,我们完整介绍了ServiceParser是怎么完整解析Service section的流程,那么在这个章节将要介绍ActionParser怎么对Action section进行庖丁解牛一一分解的,过程依然还是首先需要调用 ParseSection函数,接着利用 ParseLineSection 处理子块,解析完所有数据后,调用 EndSection。这里举几个Action section例子,以供后面分析代码参考。
on property:ro.crypto.state=unencrypted && property:ro.persistent_properties.ready=true
setprop ro.prop.load.end 1
on load_persist_props_action
load_persist_props
start logd
start logd-reinit
2.5.1 Action
??在正式开始ActionParser的解析前,首先得介绍一下Action,它定义在system/core/init/action.h中是对init中Action section的一个封装和Servcie的功能类似。它有几个非常重要的成员:
class Action {
......
std::map<std::string, std::string> property_triggers_;
std::string event_trigger_;
std::vector<Command> commands_;
......
};
??这里的Action类主要用于存放Action section相关内容即当属性变化或系统进行到程序的某个时候(event_trigger_)时触发 commands_ 向量中的一系列命令信息。ActionParser 的解析过程实际上就是解析填充这些信息。
2.5.2 ActionParser::ParseSection
??让我们按照Action section解析的顺序,先来看ParseSection,它定义在system/core/init/action_parser.cpp中。
Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
return Error() << "Actions must have a trigger";
}
Subcontext* action_subcontext = nullptr;
if (subcontexts_) {
for (auto& subcontext : *subcontexts_) {
if (StartsWith(filename, subcontext.path_prefix())) {
action_subcontext = &subcontext;
break;
}
}
}
std::string event_trigger;
std::map<std::string, std::string> property_triggers;
if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);
!result) {
return Error() << "ParseTriggers() failed: " << result.error();
}
auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,
property_triggers);
action_ = std::move(action);
return Success();
}
ParseSection处理的逻辑比较简单,主要干了如下几件事情:
- 将参数args中的内容拷贝到triggers中,并剔除字符串“on”
- 调用ParseTriggers即系action触发条件
- 将前面解析得到的触发条件为参数,构建Action
我们接着继续分析ParseTriggers:
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";
}
if (i % 2) {
if (args[i] != "&&") {
return Error() << "&& is the only symbol allowed to concatenate actions";
} else {
continue;
}
}
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 = args[i];
}
}
return Success();
}
ParseTriggers的处理逻辑也不是很复杂,主要遵循如下几个处理逻辑:
- 先比较args参数(这个是已经除去了字符串"on"的)是否是以“property:”开头的,如果是property_triggers就继续调用ParsePropertyTrigger解析
- 其它的情况就只可能是event trigger,就将args的参数赋值给event_trigger_,类型是string
革命尚未成功,让我们再接再厉继续分析ParsePropertyTrigger:
Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
std::map<std::string, std::string>* property_triggers) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
size_t equal_pos = prop_name.find('=');
if (equal_pos == std::string::npos) {
return Error() << "property trigger found without matching '='";
}
std::string prop_value(prop_name.substr(equal_pos + 1));
prop_name.erase(equal_pos);
if (!IsActionableProperty(subcontext, prop_name)) {
return Error() << "unexported property tigger found: " << prop_name;
}
if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {
return Error() << "multiple property triggers found for same property";
}
return Success();
}
ParsePropertyTrigger函数将proerty触发字符串以"="分割为name-value,然后将name-value存入property_triggers_这个map中,例如如下的触发条件字符串:
ro.crypto.state=unencrypted
经过以上步骤ParseSection就分析完了,下面还是对其流程归纳总结一下:
- ParseSection函数的作用就是构造一个Action对象
- 将trigger条件记录到Action这个对象中,如果是event trigger就赋值给event_trigger_,如果是property trigger就存放到property_triggers_这个map中
2.5.3 ActionParser::ParseLineSection
??前面章节通过ActionParser::ParseSection解析了Action的触发触发条件和创建了Action,接下来就就得解析Action section的command了,所以我们的ParseLineSection要上场了。
??ParseLineSection函数简单明了的再不过了,就是调用Action的AddCommand添加command命令。
Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
return action_ ? action_->AddCommand(std::move(args), line) : Success();
}
接着继续分析Action的方法AddCommand,定义在system/core/init/action.cpp中,AddCommand顾名思义就是添加Action section的command指令,然后调用FindFunction查找对饮给的执行函数,最后将这些信息包装成Command对象存放到commands_s队列中,这里最关键的就是FindFunction和function_map_了。
Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
if (!function_map_) {
return Error() << "no function map available";
}
auto function = function_map_->FindFunction(args);
if (!function) return Error() << function.error();
commands_.emplace_back(function->second, function->first, args, line);
return Success();
}
在前面的ServiceParser我们已经讲到了FindFunction的原理了,这里就不过多讲解了,有不清楚的可以回过头看看,这个函数主要就是通过命令查找对应的执行函数。FindFunction函数弄清楚了,那还有一个疑问function_map_是在哪里赋值的呢,这个得回到system/core/init/init.cpp里面去查看如下代码:
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
subcontexts = InitializeSubcontexts();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
通过跟进代码,我们发现function_map_是定义在system/core/init/builtins.cpp,这个实现比较简单就是直接构造一个map,然后返回. 比如{“chmod”, {2, 2, {true, do_chmod}}}, 表示命令名称叫chmod,对应的执行函数是do_chmod,允许传入的最小和最大参数数量是2。
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
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}}},
{"mount_all", {1, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
{"remount", {1, 1, {false, do_remount}}},
{"umount", {1, 1, {false, do_umount}}},
{"umount2", {1, 1, {false, do_umount2}}},
{"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}}},
};
return builtin_functions;
}
2.5.4 ActionParser::EndSection
经过万水千山,Action section终于解析完成并且封装到了Action里面去了,那么接下来需要将Action存储起来了,这就轮到了EndSection出场了。
Result<Success> ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
action_manager_->AddAction(std::move(action_));
}
return Success();
}
EndSection比较简单就是将解析完成的Action保存到ActionManager的actions_里面去,和ServiceList中的services_有异曲同工之妙。
std::vector<std::unique_ptr<Action>> actions_;
std::vector<std::unique_ptr<Service>> services_;
这里的action_manager_是在哪里赋值的呢,这个得回到system/core/init/init.cpp里面去查看如下代码:
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
subcontexts = InitializeSubcontexts();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);
到这里ActionParser解析Action section已经告一段落了,老规矩还是总结一下ActionParser中三个重要的方法都做了那些工作,这个也是对我们阶段性成果的鼓励不是:
- ParseSection函数主要是解析trigger触发条件,然后将解析的trigger构造一个Action对象
- ParseLineSection的作用主要解析Action section中的command,然后在map中查找command对应的执行函数,然后将其添加到前面构建的Action中
- EndSection将前面构建的Action加入到ActionManager的成员队里actions_里面。
2.6 ImportParser
??ActionParser和ServiceParser已经被我们完美的解决掉了,剩下的只有ImportParser了。 ??ImportParser比较简单,发现ParseLineSection、EndSection都是空实现,只实现了ParseSection和EndFile,那我们来看看它的ParseSection函。为啥这么简单,因为import就一句话的事情,就是这么简单。常见的import如下:
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
import /init.xxxdroid.common.rc
2.6.1 ImportParser::ParseSection
ParseSection函数处理逻辑比较简单,其主要流程如下:
- 首先检查单词只能是两个,因为只能是import xxx 这种语法
- 然后调用expand_props处理第二个参数,最后将结果放入数组imports_存起来
函数定义于system\core\init\import_parser.cpp。
Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
if (args.size() != 2) {
return Error() << "single argument needed for import\n";
}
std::string conf_file;
bool ret = expand_props(args[1], &conf_file);
if (!ret) {
return Error() << "error while expanding import";
}
LOG(INFO) << "Added '" << conf_file << "' to import list";
if (filename_.empty()) filename_ = filename;
imports_.emplace_back(std::move(conf_file), line);
return Success();
}
让我们接着继续解读代码,expand_props定义在system/core/init/util.cpp,其主要作用就是解析import加载的rc文件,为啥加载一个rc文件还要搞这么复杂呢。我猜测Google这么设计是为了兼容不同硬件配置和不同cpu位数的产品而为之。通过简单分析代码不难看出其作用就是找到x . y 或 {x.y}或x.y或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) {
return false;
}
while (*src_ptr) {
const char* c;
c = strchr(src_ptr, '$');
if (!c) {
dst->append(src_ptr);
return true;
}
dst->append(src_ptr, c);
c++;
if (*c == '$') {
dst->push_back(*(c++));
src_ptr = c;
continue;
} else if (*c == '\0') {
return true;
}
std::string prop_name;
std::string def_val;
if (*c == '{') {
c++;
const char* end = strchr(c, '}');
if (!end) {
LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
return false;
}
prop_name = std::string(c, end);
c = end + 1;
size_t def = prop_name.find(":-");
if (def < prop_name.size()) {
def_val = prop_name.substr(def + 2);
prop_name = prop_name.substr(0, def);
}
} else {
prop_name = c;
LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
c += prop_name.size();
}
if (prop_name.empty()) {
LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
return false;
}
std::string prop_val = android::base::GetProperty(prop_name, "");
if (prop_val.empty()) {
if (def_val.empty()) {
LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
return false;
}
prop_val = def_val;
}
dst->append(prop_val);
src_ptr = c;
}
return true;
}
2.6.2 ImportParser::EndFile
??EndFile比较简单就是遍历imports_取出其中的import文件,然后调用ParseConfig解析完整的路径,即回到前面讲述的内容了。
void ImportParser::EndFile() {
auto current_imports = std::move(imports_);
imports_.clear();
for (const auto& [import, line_num] : current_imports) {
if (!parser_->ParseConfig(import)) {
PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
<< "'";
}
}
}
2.7 SectionParser以及子类解析器总结
??通过前面的章节2.4, 2.5, 2.6三个章节我们将SectionParser的三个核心解析器ActionParser,ServiceParser,ImportParser都已经讲解完了,而这几个解析器分别实现了ParseSection、ParseLineSection、EndSection、EndFile四个函数。下面将这三个解析器放在一起总结一下:
-
ParseSection用于解析各种section的第一行,例如: service console /system/bin/sh
on early-init
import /init.${ro.zygote}.rc
-
ParseLineSection用于解析section的command和option,例如:
class core
console
disabled
user shell
group shell log readproc
seclabel u:r:shell:s0
setenv HOSTNAME console
write /proc/1/oom_score_adj -1000
write /proc/sys/kernel/sysrq 0
restorecon /adb_keys
restorecon /postinstall
mount cgroup none /acct nodev noexec nosuid cpuacct
mkdir /acct/uid
start ueventd
-
EndSection用于处理Action和Service同名的情况,以及将解析的对象存入数组备用 -
EndFile只有在ImportParser中有用到,主要是解析导入的.rc文件。
从代码层方面对init.rc的五类声明已经解析完成了,接下来上一个代码类图来总结一下init进程中五类声明的关系。
3. 加入一些事件和一些Action
??在前面的篇章中我们已经解析完了init.xx.rc中的Action section了,但是仅仅是将这些数据存储到了相对应的数据结构和_action 链表中,_action 链表中包含一些触发条件下(_trigger)的执行动作(_commands),那么这些触发条件是什么时候发生呢。这个还是需要一些额外的配置,也需要加入触发条件准备去触发。
int main(int argc, char** argv) {
......
if (false) DumpState();
am.QueueEventTrigger("init-env");
am.QueueEventTrigger("early-init");
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
am.QueueEventTrigger("init");
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
......
}
3.1 QueueEventTrigger
??QueueEventTrigger的代码定义在system/core/init/action_manager.cpp中,注意这段代码并没有真正的去触发trigger,而是将trigger字符串添加到ActionManager的event_queue_的链表中,待后续处理。
void ActionManager::QueueEventTrigger(const std::string& trigger) {
event_queue_.emplace(trigger);
}
class ActionManager {
public:
......
void QueueEventTrigger(const std::string& trigger);
......
private:
......
std::vector<std::unique_ptr<Action>> actions_;
std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
......
};
3.2 QueueBuiltinAction
??QueueBuiltinAction的代码定义在system/core/init/action_manager.cpp中,这个函数有两个参数,第一个函数是一个函数指针,第二个参数是字符串。该函数的处理逻辑如下:
- 首先通过第二个参数构建一个Action
- 把第一个参数函数指针通过AddCommand添加到前面创建的Action里面
- 最后将Action的触发条件加入到event_queue_触发队列中
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
std::map<std::string, std::string>{});
std::vector<std::string> name_vector{name};
action->AddCommand(func, name_vector, 0);
event_queue_.emplace(action.get());
actions_.emplace_back(std::move(action));
}
4. Trigger触发顺序以及监听各种触发
4.1 Trigger触发顺序以及内容
??通过QueueBuiltinAction 和QueueEventTrigger 上述两步,将需要处理的 trigger 添加到 trigger_queue_中,而 trigger_queue_本身就是一个队列,所以先加进去的,先执行,后加入的,后执行。也就是通过这种方式,init.rc 中所列 action的执行顺序得到的确认。
4.1.1 Trigger触发顺序
??我们知道trigger_queue_的触发顺序是以队列形式进行的,那么其具体流程是什么呢,这里给出一个流程图以供大家参考(这里的前提是Android源码选择了非加密模式)。
4.1.2 Trigger触发内容
??前面我们知道了Trigger的触发顺序,现在让我们看看每个Trigger主要做了那些事情。
Trigger启动阶段 | 触发内容 |
---|
early-init | 初始化第一阶段,设置 init 进程 score adj 值,重置安全上下文,启动 uevent 服务 | wait_for_coldboot_done | wait uevent_main – device_init 完成 coldboot_done 目录创建,Timeout 1s | mix_hwrng_into_linux_rng | 读取 512 bytes 硬件随机数,写入 linux RNG,不支持HWrandom 直接返回,不影响 init 启动 | keychord_init | keychord 是组合按键,keychord 为每个服务配置组合键,在服务解析时为指定服务设置相应的键码值 | console_init | 如果ro.boot.console 指定了控制台终端,那么优先使用这个控制台,如果没有指定,那么将使用默认控制台终端/dev/console | init | 创建文件系统, mount节点以及写内核变量 | late-init | 触发各种trigger,trigger early-fs fs post-fs load_system_props_action post-fs load_persist_props_action firmware_mounts_complete early-boot boot | early-fs | 设置外部存储环境变量 | fs | 专门用于加载各个分区,如mtd分区,创建adb设备目录,修改 adf 设备文件权限 | post-fs | 修改productinfo用户群组,改变系统目录访问权限(kmsg、vmallcoinfo、cache等) | load_persist_props_action | 加载property 文件如 “/system/build.prop”"/vendor/build.prop" “/factory/factory.prop” | post-fs-data | 创建、改变/data 目录以及它的子目录的访问权限,启动 vold、debuggerd 服务,bootchart_init | load_persisit_props_action | 启动logd服务,load property file /data/property,"/data/local.prop" | firmware_mounts_complete | :删除 dev/.booting 目录 | early-boot | 修改 proc、sys/class 子目录访问权限 | boot | 正常的启动命令,设置 usb 厂商参数、CPU 参数,修改 sensorhub、 bluetooth、gnss、thermal 目录访问权限,网络参数设置。 启动 Core class servic | charge | 当手机处于充电模式时(关机情况下充电), 需要执行的命令 | nonencrypted | 启动 main、late_start class service(这里的前提条件是Android 源码编译选择了非加密模式) |
4.2 监听各种触发
??如果说之前的所有工作都是往各种链表、队列里面存入信息,并没有真正去触发,而是做了重复的准备工作,前戏已经够了,那么接下来的工作就是真正去触发这些事件,以及用epoll不断监听新的事件。
int main(int argc, char** argv) {
......
while (true) {
int epoll_timeout_ms = -1;
if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
auto next_process_restart_time = RestartProcesses();
if (next_process_restart_time) {
epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_restart_time - boot_clock::now())
.count();
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
}
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
}
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
4.2.1 ExecuteOneCommand
??该代码定义在system/core/init/action_manager.cpp中,从函数名字可以看出它是执行一个Command,该函数的处理逻辑如下:
- 从EventTrigger触发队列event_queue_取出一个trigger,然后遍历所有action,找出满足trigger条件的action加入待执行列表current_executing_actions_中
- 接着从这个列表中取出一个action,执行它的第一个命令,并将命令所在下标自加1。由于ExecuteOneCommand外部是一个无限循环,因此按照上面的逻辑一遍遍执行,将按照trigger表的顺序,依次执行满足trigger条件的action,然后依次执行action中的命令
void ActionManager::ExecuteOneCommand() {
while (current_executing_actions_.empty() && !event_queue_.empty()) {
for (const auto& action : actions_) {
if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
event_queue_.front())) {
current_executing_actions_.emplace(action.get());
}
}
event_queue_.pop();
}
if (current_executing_actions_.empty()) {
return;
}
auto action = current_executing_actions_.front();
if (current_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
<< ":" << action->line() << ")";
}
action->ExecuteOneCommand(current_command_);
++current_command_;
if (current_command_ == action->NumCommands()) {
current_executing_actions_.pop();
current_command_ = 0;
if (action->oneshot()) {
auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
}
}
}
从上面的分析可以看到ExecuteOneCommand函数每次从event_queue_链表中取出一个trigger,并判断是否有_action 的触发条件匹配,如果有则依次取出匹配的 action 对象中的一个 command 命令并执行(一个action可能携带多个command)。其执行循序遵从如下逻辑:
- 当一个 action 对象所有的 command 均执行完毕后,再执行下一个action
- 当一个 trigger 触发时间点对应的 action 对象均执行完毕后,再执行下一个 trigger 对应 action。
4.2.2 监听property变化触发
通过前面我们知道Trigger触发有两种情况,第一种是直接QueueEventTrigger然后另外一种就是满足property,我们通过前面篇章源码详解Android 9.0§ 系统启动流程之init进程(第二阶段)知道但是属性系统的写操作只能在 init 进程中进行,其它进程进行属性的写操作也需要通过 init 进程。最终会调用到HandlePropertySet进行处理。这个函数处理分两种情况:
-
以ctl.开头的,会调用HandleControlMessage最终会调用到init.cpp中,通过ctl来处理ctl.start或者ctl.stop执行具体Service section,其逻辑如下 struct ControlMessageFunction {
ControlTarget target;
std::function<Result<Success>(Service*)> action;
};
static const std::map<std::string, ControlMessageFunction>& get_control_message_map() {
static const std::map<std::string, ControlMessageFunction> control_message_functions = {
{"start", {ControlTarget::SERVICE, DoControlStart}},
{"stop", {ControlTarget::SERVICE, DoControlStop}},
{"restart", {ControlTarget::SERVICE, DoControlRestart}},
{"interface_start", {ControlTarget::INTERFACE, DoControlStart}},
{"interface_stop", {ControlTarget::INTERFACE, DoControlStop}},
{"interface_restart", {ControlTarget::INTERFACE, DoControlRestart}},
};
return control_message_functions;
}
void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
const auto& map = get_control_message_map();
const auto it = map.find(msg);
if (it == map.end()) {
LOG(ERROR) << "Unknown control msg '" << msg << "'";
return;
}
std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
std::string process_cmdline;
if (ReadFileToString(cmdline_path, &process_cmdline)) {
std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' ');
process_cmdline = Trim(process_cmdline);
} else {
process_cmdline = "unknown process";
}
LOG(INFO) << "Received control message '" << msg << "' for '" << name << "' from pid: " << pid
<< " (" << process_cmdline << ")";
const ControlMessageFunction& function = it->second;
if (function.target == ControlTarget::SERVICE) {
Service* svc = ServiceList::GetInstance().FindService(name);
if (svc == nullptr) {
LOG(ERROR) << "No such service '" << name << "' for ctl." << msg;
return;
}
if (auto result = function.action(svc); !result) {
LOG(ERROR) << "Could not ctl." << msg << " for service " << name << ": "
<< result.error();
}
return;
}
if (function.target == ControlTarget::INTERFACE) {
for (const auto& svc : ServiceList::GetInstance()) {
if (svc->interfaces().count(name) == 0) {
continue;
}
if (auto result = function.action(svc.get()); !result) {
LOG(ERROR) << "Could not handle ctl." << msg << " for service " << svc->name()
<< " with interface " << name << ": " << result.error();
}
return;
}
LOG(ERROR) << "Could not find service hosting interface " << name;
return;
}
LOG(ERROR) << "Invalid function target from static map key '" << msg
<< "': " << static_cast<std::underlying_type<ControlTarget>::type>(function.target);
}
-
对于其它的属性,最终设置成功后都会调用 property_changed函数来通知 init 进程属性进行了修改,该代码在/system/core/init/init.cpp中 void property_changed(const std::string& name, const std::string& value) {
if (name == "sys.powerctl") {
shutdown_command = value;
do_shutdown = true;
}
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
LOG(INFO) << "Wait for property took " << *waiting_for_prop;
ResetWaitForProp();
}
}
}
从代码中可以看到,property_triggers_enabled 是 <属性变化触发 action> 的使能点,开启之后每次属性发生变化都会调用 ActionManager.QueuePropertyChange(name, value) 函数
void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
event_queue_.emplace(std::make_pair(name, value));
}
这个函数比较简单就是event_queue_列表添加触发条件,将已改变属性的键值对作为参数。运行队列中已经添加了该属性的变化触发条件,同样通过 am.ExecuteOneCommand() 函数遍历所有的 _actions 链表,执行相应的 commands。
4.2.3 疑问点
??通过前面的分析完了监听各种触发service,可是读者有没有发现一个问题假如是我们的service没有直接在Action中通过start启动,难道服务就不启动了吗。其实不然,通常这种服务会配置一个class的option,如下所示:
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
service healthd /sbin/healthd
class core
critical
seclabel u:r:healthd:s0
group root system wakelock
service console /system/bin/sh
class core
console
disabled
user root
group shell log readproc
seclabel u:r:shell:s0
然后会通过class_start来触发启动相关配置了class main/core的service,如下所示。这里的class_start就会触发启动相关配置的service。
5. 小结
这一阶段Init进程做了许多重要的事情,比如解析.rc文件,这里配置了所有需要执行的action和需要启动的service,Init进程根据语法一步步去解析.rc,将这些配置转换成一个个数组、队列,然后去处理这些数组、队列中的command和service,并且通过epoll监听子进程结束和属性设置.
至此,我已经将Init进程的三个阶段讲解完了,下一篇我将讲解.rc中配置的一个重要的service–zygote,它是我们app程序的鼻祖.
|