android配置启动参数,Android系统启动之配置⽂件解析
以下代码基于Android 7.0分析
简介
我们知道在Android系统启动的时候会创建Init进程,在Init进程的main()⼊⼝函数中会解析系统配置⽂件进⾏服务进程的创建和启动。
解析配置⽂件
[->system/core/init/init.cpp]
int main(int argc, char** argv) {
...
//这⾥将Action的function_map_替换为BuiltinFunctionMap
//下⽂将通过BuiltinFuntionMap的map⽅法,获取keyword对应的处理函数
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
//构造出解析⽂件⽤的parser对象
Parser& parser = Parser::GetInstance();
//为⼀些类型的关键字,创建特定的parser
parser.AddSectionParser("service",std::make_unique());
parser.AddSectionParser("on", std::make_unique());
parser.AddSectionParser("import", std::make_unique());
//开始实际的解析过程
parser.ParseConfig("/");
...
}
在解析⽂件之前,我们先来简单的介绍⼀下⽂件。⽂件是在init进程启动后执⾏的启动脚本,⽂件中记录着init进程需执⾏的操作。在Android系统中,使⽤和init.{ hardware }.rc两个⽂件。
其中⽂件在Android系统运⾏过程中⽤于通⽤的环境设置与进程相关的定义,init.{hardware}.rc(例如,⾼通有,MTK 有)⽤于定义Android在不同平台下的特定进程和环境设置等。
此处的位于system/core/中。
<⽂件⼤致分为2部分,⼀部分是以"on"关键字开头的动作列表(action list):
on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_score_adj -1000
.........
start ueventd
另⼀部分是以"service"关键字开头的服务列表(service list):
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
借助系统环境变量或Linux命令,动作列表⽤于创建所需⽬录,以及为某些特定⽂件指定权限,⽽服务列表⽤来记录init进程需要启动的⼀系列⼦进程。如上⾯代码所⽰,service关键字后第⼀个字符串表⽰服务(⼦进程)的名称,第⼆个字符串表⽰服务的执⾏路径。
接下来我们就从ParseConfig函数⼊⼿,逐步分析整个解析过程。
[->system/core/init/ init_parser.cpp]
bool Parser::ParseConfig(const std::string& path) {
//path为/
if (is_dir(path.c_str())) {
//传⼊参数为⽬录地址
return ParseConfigDir(path);
}
//传⼊参数为⽂件地址
return ParseConfigFile(path);
}
bool Parser::ParseConfigDir(const std::string& path) {
...........
std::unique_ptr
..........
/
/看起来很复杂,其实就是递归⽬录
while ((current_file = readdir(()))) {
std::string current_path = android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
if (current_file->d_type == DT_REG) {
//最终还是靠ParseConfigFile来解析实际的⽂件
if (!ParseConfigFile(current_path)) {
.............
}
}
}
}
bool Parser::ParseConfigFile(const std::string& path) {
........
std::string data;
//读取路径指定⽂件中的内容,保存为字符串形式
if (!read_file(path.c_str(), &data)) {
return false;
}
.........
//解析获取的字符串
ParseData(path, data);
.........
}
void Parser::ParseData(const std::string& filename, const std::string& data) { .......
parse_state state;
.......
std::vector<:string> args;
for (;;) {
//next_token以⾏为单位分割参数传递过来的字符串
//最先⾛到T_TEXT分⽀
switch (next_token(&state)) {
case T_EOF:
if (section_parser) {
/
/EOF,解析结束
section_parser->EndSection();
}
return;
case T_NEWLINE:
state.line++;
if (pty()) {
break;
}
//在前⽂创建parser时,我们为service,on,import定义了对应的parser
//这⾥就是根据第⼀个参数,判断是否有对应的parser
if (section_parsers_.count(args[0])) {
if (section_parser) {
//结束上⼀个parser的⼯作,将构造出的对象加⼊到对应的service_list与action_list中
section_parser->EndSection();
}
//获取参数对应的parser
section_parser = section_parsers_[args[0]].get();
std::string ret_err;
//调⽤实际parser的ParseSection函数(重点)
if (!section_parser->ParseSection(args, &ret_err)) {
parse_error(&state, "%s\n", ret_err.c_str());
section_parser = nullptr;
}
} else if (section_parser) {
std::string ret_err;
//如果第⼀个参数不是service,on,import
//则调⽤前⼀个parser的ParseLineSection函数
//这⾥相当于解析⼀个参数块的⼦项
if (!section_parser->ParseLineSection(args, state.filename, state.line, &ret_err)) {
parse_error(&state, "%s\n", ret_err.c_str());
}
}
/
/清空本次解析的数据
args.clear();
break;
parse error怎么解决case T_TEXT:
//将本次解析的内容写⼊到args中
break;
}
}
}
这⾥的解析看起来⽐较复杂,在6.0以前的版本中,整个解析是⾯向过程的。init进程统⼀调⽤⼀个函数来进⾏解析,然后在该函数中利⽤switch-case的形式,根据解析的内容进⾏相应的处理。
在Android 7.0中,为了更好地封装及⾯向对象,对于不同的关键字定义了不同的parser对象,每个对象通过多态实现⾃⼰的解析操作。
现在我们来回⼀下init进程main函数中创建的三个parse代码
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service",std::make_unique());
parser.AddSectionParser("on", std::make_unique());
parser.AddSectionParser("import", std::make_unique());
看下三个Parse的定义
class ServiceParser : public SectionParser {......}
class ActionParser : public SectionParser {......}
class ImportParser : public SectionParser {.......}
可以看到三个Parser均是继承SectionParser,具体的实现各有不同,我们以⽐较常⽤的ServiceParser和ActionParser为例,看看解析的结果如何处理。
ServiceParser
ServiceParser定义在system/core/init/service.cpp中。从前⾯的代码中我们知道,解析⼀个service块,⾸先需要调⽤ParseSection函数,接着你⽤ParseLineSection处理⼦块,在解析完数据后,调⽤EndSection。
因此,我们就着重来看下这三个函数。
[->system/core/init/service.cpp]
//根据参数来构造⼀个service对象
bool ServiceParser::ParseSection(.....) {
.......
const std::string& name = args[1];
.
......
std::vector<:string> str_args(args.begin() + 2, d());
//主要根据参数,构造出⼀个service对象
service_ = std::make_unique(name, "default", str_args);
return true;
}
//注意这⾥已经在解析⼦项了
bool ServiceParser::ParseLineSection(......) const {
//调⽤service对象的HandleLine
return service_ ? service_->HandleLine(args, err) : false;
}
bool Service::HandleLine(.....) {
........
//OptionHandlerMap继承⾃keywordMap
static const OptionHandlerMap handler_map;
//根据⼦项的内容,到对应的handler函数
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论