Android 进阶——系统启动之Android init进程解析init.rc脚本(五)
2021/8/7 7:06:01
本文主要是介绍Android 进阶——系统启动之Android init进程解析init.rc脚本(五),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章大纲
- 引言
- 一、`aosp/system/core/init.cpp#LoadBootScripts`函数加载init.rc 脚本
- 二、创建Parser
- 三、调用Parser:: ParseConfig
- 四、 Parser::ParseData 开始解析
引言
前面介绍到Android init进程创建启动完毕后,但Android 特有的adbd、Zygote、Service Manager(C++)核心进程和服务都已经启动了,是通过什么方式启动的呢?前一篇文章总结道init.rc脚本的基本语法和结构并且知道是通过解析init.rc文件完成的,具体细节是怎么样的呢,接着往下看,系列文章链接如下:
- Android 进阶——系统启动之BootLoader 简介及内核启动(一)
- Android 进阶——系统启动之Linux init进程的创建和启动(二)
- Android 进阶——系统启动之Android init进程的创建和启动(三)
- Android 进阶——系统启动之Android init.rc脚本解析(四)
- Android 进阶——系统启动之Android init进程解析init.rc脚本(五)
一、aosp/system/core/init.cpp#LoadBootScripts
函数加载init.rc 脚本
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 类中了。
二、创建Parser
aosp/system/core/init/parser.h
#ifndef _INIT_PARSER_H_ #define _INIT_PARSER_H_ #include <map> #include <memory> #include <string> #include <vector> #include "result.h" // SectionParser is an interface that can parse a given 'section' in init. // // You can implement up to 4 functions below, with ParseSection being mandatory. The first two // functions return Result<Success> indicating if they have an error. It will be reported along // with the filename and line number of where the error occurred. // // 1) ParseSection // This function is called when a section is first encountered. // // 2) ParseLineSection // This function is called on each subsequent line until the next section is encountered. // // 3) EndSection // This function is called either when a new section is found or at the end of the file. // It indicates that parsing of the current section is complete and any relevant objects should // be committed. // // 4) EndFile // This function is called at the end of the file. // It indicates that the parsing has completed and any relevant objects should be committed. namespace android { namespace init { 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(){}; }; class Parser { public: // LineCallback is the type for callbacks that can parse a line starting with a given prefix. // // They take the form of bool Callback(std::vector<std::string>&& args, std::string* err) // // Similar to ParseSection() and ParseLineSection(), this function returns bool with false // indicating a failure and has an std::string* err parameter into which an error string can // be written. using LineCallback = std::function<Result<Success>(std::vector<std::string>&&)>; Parser(); bool ParseConfig(const std::string& path); bool ParseConfig(const std::string& path, size_t* parse_errors); void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser); void AddSingleLineParser(const std::string& prefix, LineCallback callback); private: void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors); bool ParseConfigFile(const std::string& path, size_t* parse_errors); bool ParseConfigDir(const std::string& path, size_t* parse_errors); std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_; std::vector<std::pair<std::string, LineCallback>> line_callbacks_; }; } // namespace init } // namespace android #endif
aosp/system/core/init/parser.cpp
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; } void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) { section_parsers_[name] = std::move(parser); }
三、调用Parser:: ParseConfig
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); }
ParseConfigFile中通过ReadFile把文件读入内存,存放到自动变量config_contents再传入ParseData函数
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'); // TODO: fix parse_config. 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; }
四、 Parser::ParseData 开始解析
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 std::vector<char> data_copy(data.begin(), data.end());//定义一个向量(可变数组)临时存储文件的原始数据 data_copy.push_back('\0'); /* *core/init/tokenizer.h parse_state结构体 * struct parse_state { char *ptr; char *text; int line; int nexttoken; }; * */ parse_state state; state.line = 0; state.ptr = &data_copy[0];//向量首地址初始化赋值给ptr state.nexttoken = 0; SectionParser* section_parser = nullptr; int section_start_line = -1; std::vector<std::string> args; //c++ 11 以上的lambda 语法 ,可以理解为一个匿名函数,end_section() 就是调用形式 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; // 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. 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; } } }
未完待续…
这篇关于Android 进阶——系统启动之Android init进程解析init.rc脚本(五)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-27文件掩码什么意思?-icode9专业技术文章分享
- 2024-12-27如何使用循环来处理多个订单的退款请求,代码怎么写?-icode9专业技术文章分享
- 2024-12-27VSCode 在编辑时切换到另一个文件后再切回来如何保持在原来的位置?-icode9专业技术文章分享
- 2024-12-27Sealos Devbox 基础教程:使用 Cursor 从零开发一个 One API 替代品 审核中
- 2024-12-27TypeScript面试真题解析与实战指南
- 2024-12-27TypeScript大厂面试真题详解与解析
- 2024-12-26怎么使用nsenter命令进入容器?-icode9专业技术文章分享
- 2024-12-26导入文件提示存在乱码,请确定使用的是UTF-8编码怎么解决?-icode9专业技术文章分享
- 2024-12-26csv文件怎么设置编码?-icode9专业技术文章分享
- 2024-12-25TypeScript基础知识详解