参考:
https://blog.csdn.net/m0_49328056/article/details/121883742
https://blog.csdn.net/u012944254/article/details/101453486
接前文 http://www.gaohaiyan.com/4035.html
init流程的第二阶段SecondStageMain,这个函数中加载rc文件并解析,及启动各种service、action,监听事件。
1.rc文件
system/core/rootdir/init.rc
文本内容,如果一行文本以“import、on、service”开始,就认为是一个‘section’。如果以“#”开始表示注释。
当有事件触发一个Action后,这个action的command自上而下依次执行。
Action的tigger有如下几种:
1 2 3 4 5 6 7 8 9 10 11 12 |
on boot #系统启动触发 on early-init #在初始化之前触发 on init #在初始化时触发(在启动配置文件/init.conf被装载之后) on late-init #在初始化晚期阶段触发 on charger #当充电时触发 on property:<key>=<value> #当属性值满足条件时触发 on post-fs #挂载文件系统 on post-fs-data #挂载data on device-added-<path> #在指定设备被添加时触发 on device-removed-<path> #在指定设备被移除时触发 on service-exited-<name> #在指定service退出时触发 on <name>=<value> #当属性<name>等于<value>时触发 |
command有如下几种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
chdir <dirc> 更改工作目录为<dirc> chmod <octal-mode> <path> 更改文件访问权限 chown <owner> <group> <path> 更改文件所有者和组群 chroot <direc> 更改根目录位置 class_start <serviceclass> 如果它们不在运行状态的话,启动由<serviceclass>类名指定的所有相关服务 class_stop <serviceclass> 如果它们在运行状态的话,停止 domainname <name> 设置域名 exec <path> [ <argument> ]* fork并执行一个程序,其路径为<path>,这条命令将阻塞直到该程序启动完成,因此它有可能造成init程序在某个节点不停地等待 export <name> <value> 设置某个环境变量<name>的值为<value>,这是对全局有效的,即其后所有进程都将继承这个变量 hostname <name> 设置主机名 ifup <interface> 使网络接口<interface>成功连接 import <filename> 引入一个名为<filename>的文件 insmod <path> 在<path>路径上安装一个模块 mkdir <path> [mode] [owner] [group] 在<path>路径上新建一个目录 mount <type> <device> <dir> [<mountoption>]* 尝试在指定路径上挂载一个设备 setprop <name> <value> 设置系统属性<name>的值为<value> setrlinit <resource> <cur> <max> 设置一种资源的使用限制。这个概念亦存在于Linux系统中,<cur>表示软限制,<max>表示硬限制 start <service> 启动一个服务 stop <service> 停止一个服务 symlink <target> <path> 创建一个<path>路径的软链接,目标为<target> sysclk <mins_west_of_gmt> 设置基准时间,如果当前时间时GMT,这个值是0 trigger <event> 触发一个事件 write <path> <string> [<string>]* 打开一个文件,并写入字符串 |
Service是可执行程序,它们在特定选项的约束下会被init程序运行或者重启(service可以在配置中指定是否需要退出时重启,这样当service出现异常或者程序crash时候就有机会复原)
Service的Options有如下几种:
1 2 3 4 5 6 7 8 9 10 |
class <name> 为该服务指定一个class名,同一个class的所有服务必须同时启动或者停止。 默认情况下服务的class名是“default”。另外还有core(其它服务依赖的基础性核心服务)、main(java须要的基本服务)、late_start(厂商定制的服务) critical 表示这是一个对设备至关重要的一个服务,如果它在四分钟内退出超过四次,则设备将重启进入恢复模式 disabled 此服务不会自动启动,而是需要通过显式调用服务名来启动 group <groupname> [<groupname>]* 在启动服务前将用户组切换为<groupname> oneshot 只启动一次,当此服务退出时,不要主动去重启它 onrestart 当此服务重启时,执行某些命令 setenv <name> <value> 设置环境变量<name>为某个值<value> socket <name> <type> <perm> [ <user> [<group>]] 创建一个名为/dev/socket/<name>的unix domain socket,然后将它的fd值传给启动它的进程,有效的<type>值包括dgram,stream和seqacket,而user和group的默认值是0 user <username> 在启动服务前将用户组切换为<username>,默认情况下用户都是root |
2. rc文件的加载和解析过程
SecondStageMain --> LoadBootScripts --> ParseConfig --> ParseConfigFile --> ParseData --> ParseSection(3种)
代码:
%lineage%/system/core/init/init.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// 入口 int SecondStageMain(int argc, char** argv) { // .... ActionManager& am = ActionManager::GetInstance(); ServiceList& sm = ServiceList::GetInstance(); LoadBootScripts(am, sm); //【--- 1 -->】 // .... am.QueueEventTrigger("early-init"); // .... am.QueueEventTrigger("init"); // .... am.QueueEventTrigger("late-init"); // .... while (true) { // .... if ( !( prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running() ) ) { am.ExecuteOneCommand(); } // .... } return 0; } // 加载,解析init.rc文件 static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) { Parser parser = CreateParser(action_manager, service_list); // .... parser.ParseConfig(bootscript); //【--- 2 -->】 } Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) { // 创建解析器 Parser parser;// k-v的形式,添加3项内容。http://www.javashuo.com/article/p-adnqlkxs-bk.html parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt)); parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext())); parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser)); return parser; } |
↓
%lineage%/system/core/init/parser.cpp 约184行 bool Parser::ParseConfig(const std::string& path)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
bool Parser::ParseConfig(const std::string& path) { if (is_dir(path.c_str())) { return ParseConfigDir(path); // 是目录的,遍历rc文件,再去ParseConfigFile进行解析 } return ParseConfigFile(path); // 约142行。解析 【--- 3 -->】 } bool Parser::ParseConfigFile(const std::string& path) { // 解析rc文件 // .... ParseData(path, &config_contents.value()); // 约44行。封装 【--- 4 -->】 // .... } // filename:rc文件的路径。data:rc文件的文本内容 void Parser::ParseData(const std::string& filename, std::string* data) { // ...... auto end_section = [&] { // ...... if (auto result = section_parser->EndSection(); !result.ok()) { // section结束【 ActionParser->EndSection() 】 // ...... } // ...... }; for (;;) { // 循环读取state switch (next_token(&state)) { case T_EOF: // 0 行结束 end_section(); for (const auto& [section_name, section_parser] : section_parsers_) { section_parser->EndFile(); // 文件结束【 ImportParser->EndFile() 】 } return; case T_NEWLINE: { // 2 新的一行文本内容 // ...... if (line_callback != line_callbacks_.end()) { end_section(); // ...... } else if (section_parsers_.count(args[0])) { // 是import或on或service // ...... section_parser->ParseSection(std::move(args), filename, state.line); // 使用匹配的解析器解析【 3种SectionParse都有->ParseSection() 】 // ...... } else if (section_parser) { // 是command或option, if (auto result = section_parser->ParseLineSection(std::move(args), state.line); // 使用已有的解析器解析 【 3种SectionParse都有->ParseSection() 】 !result.ok()) { // ...... } } else if (!bad_section_found) { // ...... } // ...... } case T_TEXT: // ...... } // end switch } // end for } |
- end
本文由崔维友 威格灵 cuiweiyou vigiles cuiweiyou 原创,转载请注明出处:http://www.gaohaiyan.com/4047.html
承接App定制、企业web站点、办公系统软件 设计开发,外包项目,毕设