[android] init进程 .rc文件中service、action的parsing
init进程code位置:system/core/init
system/core/init/README.md,这个文件是描述rc文件语法的。
在.rc文件中,有3中类型:
1. service
2. on(action)
3. import
init.cpp
1 2 3 4 5 6 7 8 9 | 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; } |
以service开头,表示是service;以on开头表示是Action。
对于service行或者on行,调用ParseSection(),在ParseSection中会创建Servcie或者Action对象。
然后处理service或者on行后的行(service或者action的子行)。对于每个子行,都会调用ParseLineSection(),在这个函数中,对于service,会执行对应的函数,比如对于writepid,会调用Servcie::ParseWritepid();
对于action,会执行AddCommand()。
等这个service或者action的所有子行都parse完后,在parse下一个servcie或者action前(调用ParseSection之前),会call endSection(),这个函数会call EndSection。在EndSection()中,对于service,会执行add service;对于action,会add action到action manager。
对于service,对应文件是service.cpp;对于action,对应文件是action_parser.cpp。
parse .rc文件的入口:init.cpp/LoadBootScripts()
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 | 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; } 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); } } |
ParseData()函数中通过next_token(&state)将一行中的所有单词(以比如空格分隔)走T_TEXT case调用args.emplace_back(state.text)加到args中。当一行结束时(\n符),next_token()会返回T_NEWLINE,在T_NEWLINE case中处理这一行。处理完后,将args clear。
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 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' ); parse_state state; state.line = 0; state.ptr = &data_copy[0]; state.nexttoken = 0; SectionParser* section_parser = nullptr ; int section_start_line = -1; std::vector<std::string> args; 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 ; } } } |
service class有一个Action的成员--onrestart_
在parse一个service时,例如下面的audioserver.rc,对于service里的onrestart行,就是对应一个command。这个command会被add到onrestart_ Action中,是通过如下的方法add的:
在service.cpp里有一个Service::OptionParserMap::map()函数,根据key onrestart找到其对应的函数:Service::ParseOnrestart(),然后执行这个函数。这个函数会调用Action.cpp中的addcommand函数。
frameworks/av/media/audioserver/audioserver.rc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | service audioserver /system/bin/audioserver class core user audioserver # media gid needed for /dev/fm (radio) and for /data/misc/media (tee) group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct ioprio rt 4 writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks onrestart restart vendor.audio-hal-2-0 # Keep the original service name for backward compatibility when upgrading # O-MR1 devices with framework-only. onrestart restart audio-hal-2-0 on property:vts.native_server.on=1 stop audioserver on property:vts.native_server.on=0 start audioserver |
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 | const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { constexpr std:: size_t kMax = std::numeric_limits<std:: size_t >::max(); // clang-format off static const Map option_parsers = { { "capabilities" , {1, kMax, &Service::ParseCapabilities}}, { "class" , {1, kMax, &Service::ParseClass}}, { "console" , {0, 1, &Service::ParseConsole}}, { "critical" , {0, 0, &Service::ParseCritical}}, { "disabled" , {0, 0, &Service::ParseDisabled}}, { "enter_namespace" , {2, 2, &Service::ParseEnterNamespace}}, { "group" , {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}}, { "interface" , {2, 2, &Service::ParseInterface}}, { "ioprio" , {2, 2, &Service::ParseIoprio}}, { "priority" , {1, 1, &Service::ParsePriority}}, { "keycodes" , {1, kMax, &Service::ParseKeycodes}}, { "oneshot" , {0, 0, &Service::ParseOneshot}}, { "onrestart" , {1, kMax, &Service::ParseOnrestart}}, { "override" , {0, 0, &Service::ParseOverride}}, { "oom_score_adjust" , {1, 1, &Service::ParseOomScoreAdjust}}, { "memcg.swappiness" , {1, 1, &Service::ParseMemcgSwappiness}}, { "memcg.soft_limit_in_bytes" , {1, 1, &Service::ParseMemcgSoftLimitInBytes}}, { "memcg.limit_in_bytes" , {1, 1, &Service::ParseMemcgLimitInBytes}}, { "namespace" , {1, 2, &Service::ParseNamespace}}, { "rlimit" , {3, 3, &Service::ParseProcessRlimit}}, { "seclabel" , {1, 1, &Service::ParseSeclabel}}, { "setenv" , {2, 2, &Service::ParseSetenv}}, { "shutdown" , {1, 1, &Service::ParseShutdown}}, { "socket" , {3, 6, &Service::ParseSocket}}, { "file" , {2, 2, &Service::ParseFile}}, { "user" , {1, 1, &Service::ParseUser}}, { "writepid" , {1, kMax, &Service::ParseWritepid}}, }; // clang-format on return option_parsers; } |
Action.cpp中的addcommand函数会根据builtins.cpp中的Map builtin_fucnctions找到对应的函数,比如上面例子中的onrestart restart vendor.audio-hal-2-0,就是根据key restart找到对应的函数do_restart。
然后会根据找到的这个函数和args等其它参数构造一个Command,然后将这个Command对象add到commands_中。
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 57 | const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { constexpr std:: size_t kMax = std::numeric_limits<std:: size_t >::max(); // clang-format off static const Map builtin_functions = { { "bootchart" , {1, 1, { false , do_bootchart}}}, { "chmod" , {2, 2, { true , do_chmod}}}, { "chown" , {2, 3, { true , do_chown}}}, { "class_reset" , {1, 1, { false , do_class_reset}}}, { "class_restart" , {1, 1, { false , do_class_restart}}}, { "class_start" , {1, 1, { false , do_class_start}}}, { "class_stop" , {1, 1, { false , do_class_stop}}}, { "copy" , {2, 2, { true , do_copy}}}, { "domainname" , {1, 1, { true , do_domainname}}}, { "enable" , {1, 1, { false , do_enable}}}, { "exec" , {1, kMax, { false , do_exec}}}, { "exec_background" , {1, kMax, { false , do_exec_background}}}, { "exec_start" , {1, 1, { false , do_exec_start}}}, { "export" , {2, 2, { false , do_export}}}, { "hostname" , {1, 1, { true , do_hostname}}}, { "ifup" , {1, 1, { true , do_ifup}}}, { "init_user0" , {0, 0, { false , do_init_user0}}}, { "insmod" , {1, kMax, { true , do_insmod}}}, { "installkey" , {1, 1, { false , do_installkey}}}, { "load_persist_props" , {0, 0, { false , do_load_persist_props}}}, { "load_system_props" , {0, 0, { false , do_load_system_props}}}, { "loglevel" , {1, 1, { false , do_loglevel}}}, { "mkdir" , {1, 4, { true , do_mkdir}}}, // TODO: Do mount operations in vendor_init. // mount_all is currently too complex to run in vendor_init as it queues action triggers, // imports rc scripts, etc. It should be simplified and run in vendor_init context. // mount and umount are run in the same context as mount_all for symmetry. { "mount_all" , {1, kMax, { false , do_mount_all}}}, { "mount" , {3, kMax, { false , do_mount}}}, { "umount" , {1, 1, { false , do_umount}}}, { "readahead" , {1, 2, { true , do_readahead}}}, { "restart" , {1, 1, { false , do_restart}}}, { "restorecon" , {1, kMax, { true , do_restorecon}}}, { "restorecon_recursive" , {1, kMax, { true , do_restorecon_recursive}}}, { "rm" , {1, 1, { true , do_rm}}}, { "rmdir" , {1, 1, { true , do_rmdir}}}, { "setprop" , {2, 2, { true , do_setprop}}}, { "setrlimit" , {3, 3, { false , do_setrlimit}}}, { "start" , {1, 1, { false , do_start}}}, { "stop" , {1, 1, { false , do_stop}}}, { "swapon_all" , {1, 1, { false , do_swapon_all}}}, { "symlink" , {2, 2, { true , do_symlink}}}, { "sysclktz" , {1, 1, { false , do_sysclktz}}}, { "trigger" , {1, 1, { false , do_trigger}}}, { "verity_load_state" , {0, 0, { false , do_verity_load_state}}}, { "verity_update_state" , {0, 0, { false , do_verity_update_state}}}, { "wait" , {1, 2, { true , do_wait}}}, { "wait_for_prop" , {2, 2, { false , do_wait_for_prop}}}, { "write" , {2, 2, { true , do_write}}}, }; // clang-format on return builtin_functions; } |
对于service来说,只有onrestart才有对应的command,像writepid、group等等其它都是没有对应的command的。
对于service定义的command(onrestart),什么时候执行这个command呢?
这个是在service.cpp中的onrestart_.ExecuteAllCommands()的时候去执行的。
这个onrestart_.ExecuteAllCommands()对于media进程来说,它没有添加command(如下面的mediaserver.rc),所以不会做什么事情。
frameworks/av/media/mediaserver/mediaserver.rc
1 2 3 4 5 6 | service media /system/bin/mediaserver class main user media group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm ioprio rt 4 writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析