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 init进程分析 init脚本解析和处理

Android 进阶——系统启动之Android init.rc脚本解析

Android 进阶——系统启动之Android init.rc脚本解析

Android 进阶——系统启动之Android init进程解析init.rc脚本

Android 进阶——系统启动之Android init进程解析init.rc脚本

Android的init过程:init.rc解析流程