Android 12 init rc脚本解析和事件执行流程
Posted pecuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 12 init rc脚本解析和事件执行流程相关的知识,希望对你有一定的参考价值。
文章托管在gitee上 Android Notes , 同步csdn
本文基于android12 分析
在init启动的SecondStage,通过调用LoadBootScripts来解析启动脚本,也就是rc文件。下面来研究这个解析过程,之后再看看init如何处理事件并执行相关action的。
SecondStageMain
int SecondStageMain(int argc, char** argv)
...
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map); // 设置 Action 需要的函数映射表
...
InitializeSubcontext(); // 初始化Subcontext
ActionManager& am = ActionManager::GetInstance(); // 创建action管理器
ServiceList& sm = ServiceList::GetInstance(); // 创建服务管理列表
LoadBootScripts(am, sm); // 加载并解析启动脚本
...
InitializeSubcontext
创建subcontext进程,用于执行init交给其的一些任务(比如后面执行的某些Command),通过socket与init进行通信
/// @system/core/init/subcontext.cpp
void InitializeSubcontext()
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__)
subcontext.reset( // 创建Subcontext, path_prefixes为"/vendor", "/odm"
new Subcontext(std::vector<std::string>"/vendor", "/odm", kVendorContext));
/// @system/core/init/subcontext.h
Subcontext(std::vector<std::string> path_prefixes, std::string context, bool host = false)
: path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0)
if (!host) /// host默认为false
Fork(); // 创建subcontext进程
LoadBootScripts
加载并解析 init rc 脚本
/// @system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list)
// 创建 Parser 对象
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");//获取属性指定的rc
if (bootscript.empty()) // 没有指定rc,解析默认位置
parser.ParseConfig("/system/etc/init/hw/init.rc"); // 首先解析 init.rc
if (!parser.ParseConfig("/system/etc/init")) // 解析 /system/etc/init 目录
late_import_paths.emplace_back("/system/etc/init"); // 解析失败延时解析
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init"); // 解析 /system_ext/etc/init 目录
if (!parser.ParseConfig("/vendor/etc/init")) // 解析 /vendor/etc/init 目录
late_import_paths.emplace_back("/vendor/etc/init");
if (!parser.ParseConfig("/odm/etc/init")) // 解析 /odm/etc/init 目录
late_import_paths.emplace_back("/odm/etc/init");
if (!parser.ParseConfig("/product/etc/init")) // 解析 /product/etc/init 目录
late_import_paths.emplace_back("/product/etc/init");
else
parser.ParseConfig(bootscript);
CreateParser
/// @system/core/init/init.cpp
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list)
Parser parser;
// 添加各部分解析器
// 创建ServiceParser 解析 service 服务,用于管理和启动native进程
parser.AddSectionParser("service", std::make_unique<ServiceParser>(
&service_list, GetSubcontext(), std::nullopt));
// 创建ActionParser 解析 on action , 定义的一些动作
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
// ImportParser解析import, 导入其他rc
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
AddSectionParser按名称添加parser到集合
/// @system/core/init/parser.cpp
void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser)
section_parsers_[name] = std::move(parser); // 按名称存储对应的parser
// 集合按名字存储对应的 SectionParser
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
接下来看解析流程,通过调用Parser::ParseConfig函数来实现解析指定路径的rc文件或目录下的rc文件。
Parser::ParseConfig
/// @system/core/init/parser.cpp
bool Parser::ParseConfig(const std::string& path)
if (is_dir(path.c_str())) // 如果提供的目录
return ParseConfigDir(path); // 解析配置目录
return ParseConfigFile(path); // 解析配置文件
Parser::ParseConfigDir
执行解析配置目录操作,遍历目录调用ParseConfigFile解析每一个文件。忽略目录下的子目录,也就是不支持多层级目录。
/// @system/core/init/parser.cpp
bool Parser::ParseConfigDir(const std::string& path)
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir)
PLOG(INFO) << "Could not import directory '" << path << "'";
return false;
dirent* current_file;
std::vector<std::string> files;
while ((current_file = readdir(config_dir.get()))) // 读取目录下的内容
// Ignore directories and only process regular files.
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);
// Sort first so we load files in a consistent order (bug 31996208)
std::sort(files.begin(), files.end());
for (const auto& file : files) // 遍历集合,解析文件
if (!ParseConfigFile(file))
LOG(ERROR) << "could not import file '" << file << "'";
return true;
接下来看如何解析一个配置文件
Parser::ParseConfigFile
/// @system/core/init/parser.cpp
bool Parser::ParseConfigFile(const std::string& path)
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
// 读取文件内容到string,实现在system/core/init/util.cpp,调用 android::base::ReadFdToString
auto config_contents = ReadFile(path);
if (!config_contents.ok())
LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
return false;
ParseData(path, &config_contents.value()); // 解析脚本内容
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
Parser::ParseData
解析流程循环:
- 获取的下一个token来决定接下来的动作,比如EOF结尾或者某个关键字
- 遇到T_EOF则调用EndSection结束当前解析的ParseSection,退出循环
- 遇到T_NEWLINE换新行,则进行解析一行
- 如果获取的token是parser对应的关键字,比如on,则获取对应的parser进行解析,调用ParseSection
- 如果token不是关键字,且上一步有获取parser,则进行解析一行,调用ParseLineSection
- 遇到下一个关键字则调用EndSection结束当前解析的ParseSection,开启新的解析
- 如果解析到文本,则添加到args集合
void Parser::ParseData(const std::string& filename, std::string* data)
data->push_back('\\n'); // 添加一个换行
data->push_back('\\0'); // 添加一个结束符
parse_state state;
state.line = 0;
state.ptr = data->data();
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
int section_start_line = -1;
std::vector<std::string> args;
// If we encounter a bad section start, there is no valid parser object to parse the subsequent
// sections, so we must suppress errors until the next valid section is found.
bool bad_section_found = false;
auto end_section = [&] // 结束section的回调
bad_section_found = false;
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result.ok())
parse_error_count_++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
section_parser = nullptr;
section_start_line = -1;
;
for (;;)
switch (next_token(&state)) // 根据获取的下一个token来决定接下来的动作
case T_EOF: // 文件结束。
end_section();
for (const auto& [section_name, section_parser] : section_parsers_)
section_parser->EndFile();
return;
case T_NEWLINE: // 检测到换行,解析当前行
state.line++;
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.
auto line_callback = std::find_if(
line_callbacks_.begin(), line_callbacks_.end(),
[&args](const auto& c) return android::base::StartsWith(args[0], c.first); );
if (line_callback != line_callbacks_.end()) // 针对指定prefix的情况,比如 /dev/
end_section();
if (auto result = line_callback->second(std::move(args)); !result.ok())
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
else if (section_parsers_.count(args[0])) // 如果第一个值能够匹配到parser, 比如是 service
end_section(); // 结束之前。
section_parser = section_parsers_[args[0]].get();
section_start_line = state.line;
if (auto result = // 解析 section
section_parser->ParseSection(std::move(args), filename, state.line);
!result.ok())
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
bad_section_found = true;
else if (section_parser) // 使用当前parser解析一行
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result.ok())
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
else if (!bad_section_found) // 没有任何匹配,则表示关键字错误
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line
<< ": Invalid section keyword found";
args.clear();
break;
case T_TEXT: // 是文本则添加到vector
args.emplace_back(state.text);
break;
next_token
该函数是用来解析token的,并获取有效的文本信息
/// @system/core/init/tokenizer.cpp
int next_token(struct parse_state *state)
char *x = state->ptr;
char *s;
if (state->nexttoken)
int t = state->nexttoken;
state->nexttoken = 0;
return t;
for (;;)
switch (*x)
case 0: // 结尾
state->ptr = x;
return T_EOF;
case '\\n': // 换行符
x++;
state->ptr = x;
return T_NEWLINE;
case ' ':
case '\\t':
case '\\r': // 跳过空白字符
x++;
continue;
case '#':
while (*x && (*x != '\\n')) x++; // 跳过 # 注释行
if (*x == '\\n') // 换行
state->ptr = x+1;
return T_NEWLINE;
else // 结尾
state->ptr = x;
return T_EOF;
default: // 默认跳转到文本处理
goto text;
textdone:
state->ptr = x;
*s = 0;
return T_TEXT;
text:
state->text = s = x;
textresume:
for (;;)
switch (*x)
case 0: // 字符结束符
goto textdone;
case ' ':
case '\\t':
case '\\r': // 检测到空白符则返回
x++;
goto textdone;
case '\\n': // 换行
state->nexttoken = T_NEWLINE;
x++;
goto textdone;
case '"': // 引号包裹内存
x++;
for (;;)
switch (*x)
case 0:
/* unterminated quoted thing */
state->ptr = x;
return T_EOF;
case '"':
x++;
goto textresume;
default:
*s++ = *x++; // 复制引号内容
break;
case '\\\\': // 下面处理其他转义字符
x++以上是关于Android 12 init rc脚本解析和事件执行流程的主要内容,如果未能解决你的问题,请参考以下文章
Android 进阶——系统启动之Android init.rc脚本解析
Android 进阶——系统启动之Android init.rc脚本解析
Android 进阶——系统启动之Android init进程解析init.rc脚本