Android系统启动2————Android初始化语言概述和解析

悠悠 2021-11-09 15:30 530阅读 0赞

Android系统启动2————Android初始化语言概述和解析

一.概述

在Android初始化语言包含了4种类型的声明,Actions(行动),Commands(命令)、Services(服务)和Options(选项)。

所以这些都是以行为单位,各种记号由空格隔开。反斜杠可用于在记号间插入空格,双引号也可以用于防止字符串被空格分隔成多个记号。行末的反斜杠用于折行。
注释行以井号(#)开头(允许以空格开头)。

Actions和Services声明一个新的分组。所有的命令或选项都属于最近申明的分组。位于第一个分组之前的命令或选项将会被忽略。
Actions和Services有唯一的名字。如果有重名的情况,第二个申明的将会被作为错误忽略。

二.Actions

Actions其实就是一序列的Commands(命令)。Actions都有一个trigger(触发器),它被用于决定action的执行时间。当一个符合action触发条件的事件发生时,action会被加入到执行队列的末尾,除非它已经在队列里了。
队列中的每一个action都被依次提取出,而这个action中的每个command(命令)都将被依次执行。Init在这些命令的执行期间还控制着其他的活动(设备节点的创建和注销、属性的设置、进程的重启)。

1.Actions的形式

  1. on <trigger>
  2. <command>
  3. <command>
  4. <command>

示例

  1. on boot
  2. ifup lo
  3. hostname localhost
  4. domainname localdomain

上面的示例中,boot是触发器,下面的三行是command

2.触发器






























触发器 含义
boot 在init执行后出发,也就是在init.rc被装载后执行该trigger
<name>=<value> 当属性被设置成时被触发
device-added-<path> 当设备节点被添加时触发
device-removed-<path> 当设备节点被移除时添加
service-exited-<name> 会在一个特定的服务退出时触发

3.命令














































































































命令 含义
exec <path> [<argument> ] 创建和执行一个程序(<path>)。在程序完全执行前,init将会阻塞。 尽量避免使用 ,它可能会引起init执行超时
export <name> <value> 在全局环境中将 <name>变量的值设为<value>
ifup <interface> 启动网络接口
import <filename> 指定要解析的其他配置文件
hostname <name> 设置主机名
chdir <directory> 改变工作目录
chmod <octal-mode><path> 改变文件的访问权限
chown <owner><group> <path> 更改文件的所有者和组
chroot <directory> 改变处理根目录
class_start<serviceclass> 启动所有指定服务类下的未运行服务
class_stop<serviceclass> 停止指定服务类下的所有已运行的服务。
domainname <name> 设置域名
insmod <path> 加载<path>指定的驱动模块
mkdir <path> [mode][owner] [group] 创建一个目录<path> ,可以选择性地指定mode、owner以及group。如果没有指定,默认的权限为755,并属于root用户和 root组。
mount <type> <device> <dir> [<mountoption> ] 试图在目录<dir>挂载指定的设备
setkey 保留,暂时未用
setprop <name><value> 将系统属性<name>的值设为<value>
setrlimit <resource> <cur> <max> 设置<resource>的rlimit (资源限制)
start <service> 启动指定服务(如果此服务还未运行)
stop<service> 停止指定服务(如果此服务在运行中)
symlink <target> <path> 创建一个指向<path>的软连接<target>
sysclktz <mins_west_of_gmt> 设置系统时钟基准
trigger <event> 触发一个事件。用于Action排队
wait <path> [<timeout> ] 等待一个文件是否存在,当文件存在时立即返回,或到<timeout>指定的超时时间后返回,如果不指定<timeout>,默认超时时间是5秒。
write <path> <string> [ <string> ]* 向<path>指定的文件写入一个或多个字符串

三.Services

Services(服务)是一个程序,他在初始化时启动,并在退出时重启(可选)

1.Services 形式

  1. service <name> <pathname> [ <argument> ]* //<service的名字><执行程序路径><传递参数>
  2. <option>
  3. <option>

示例

  1. service servicemanager /system/bin/servicemanager
  2. class core
  3. user system
  4. group system
  5. critical
  6. onrestart restart zygote
  7. onrestart restart media
  8. onrestart restart surfaceflinger
  9. onrestart restart drm

Services的选项是服务的修饰符,可以影响服务如何以及怎样运行

2.选项

  • critical,明这是一个非常重要的服务。如果该服务4分钟内退出大于4次,系统将会重启并进入 Recovery (恢复)模式。
  • disabled,表明这个服务不会同与他同trigger (触发器)下的服务自动启动。该服务必须被明确的按名启动。
  • setenv ,在进程启动时将环境变量设置为
  • socket [ [ ] ], 创建一个unix域的名为/dev/socket/ 的套接字,并传递它的文件描述符给已启动的进程。 必须是 “dgram”,“stream” 或”seqpacket”。用户和组默认是0。
  • user ,在启动这个服务前改变该服务的用户名。
  • group [ ]*,在启动这个服务前改变该服务的组名。除了(必需的)第一个组名,附加的组名通常被用于设置进程的补充组(通过setgroups函数),档案默认是root。
  • oneshot, 服务退出时不重启。
  • class ,指定一个服务类。所有同一类的服务可以同时启动和停止。如果不通过class选项指定一个类,则默认为”default”类服务。
  • onrestart,当服务重启,执行一个命令

四.分析rc文件

在Android启动过程中,通过下面代码来分析rc文件.
源码版本:Android 8.0

  1. parser.ParseConfig("/init.rc");

1.Parser类

Parser类的定义在parser.h中

目录:system/core/init/init_parser.h

  1. class SectionParser {
  2. public:
  3. virtual ~SectionParser() {
  4. }
  5. virtual bool ParseSection(const std::vector<std::string>& args,
  6. std::string* err) = 0;
  7. virtual bool ParseLineSection(const std::vector<std::string>& args,
  8. const std::string& filename, int line,
  9. std::string* err) const = 0;
  10. virtual void EndSection() = 0;
  11. virtual void EndFile(const std::string& filename) = 0;
  12. };
  13. class Parser {
  14. public:
  15. static Parser& GetInstance();
  16. void DumpState() const;
  17. bool ParseConfig(const std::string& path);
  18. void AddSectionParser(const std::string& name,
  19. std::unique_ptr<SectionParser> parser);
  20. void set_is_system_etc_init_loaded(bool loaded) {
  21. is_system_etc_init_loaded_ = loaded;
  22. }
  23. void set_is_vendor_etc_init_loaded(bool loaded) {
  24. is_vendor_etc_init_loaded_ = loaded;
  25. }
  26. void set_is_odm_etc_init_loaded(bool loaded) {
  27. is_odm_etc_init_loaded_ = loaded;
  28. }
  29. bool is_system_etc_init_loaded() {
  30. return is_system_etc_init_loaded_; }
  31. bool is_vendor_etc_init_loaded() {
  32. return is_vendor_etc_init_loaded_; }
  33. bool is_odm_etc_init_loaded() {
  34. return is_odm_etc_init_loaded_; }
  35. private:
  36. Parser();
  37. void ParseData(const std::string& filename, const std::string& data);
  38. bool ParseConfigFile(const std::string& path);
  39. bool ParseConfigDir(const std::string& path);
  40. std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
  41. bool is_system_etc_init_loaded_ = false;
  42. bool is_vendor_etc_init_loaded_ = false;
  43. bool is_odm_etc_init_loaded_ = false;
  44. };

parser包含一个比较重要的成员变量

  • section_parsers_,是负责解析语法的,在初始化的时候添加了三个解析类ServiceParser、ActionParser、ImportParser,通过AddSectionParser函数添加

目录;system/core/init/init_parser.cpp

  1. void Parser::AddSectionParser(const std::string& name,
  2. std::unique_ptr<SectionParser> parser) {
  3. section_parsers_[name] = std::move(parser);
  4. }

2.parser.ParseConfig

分析完Parser类,我们继续来看看ParseConfig,即Parser对外提供的解析.rc方法

目录:system/core/init/init_parser.cpp

  1. bool Parser::ParseConfig(const std::string& path) {
  2. if (is_dir(path.c_str())) {
  3. return ParseConfigDir(path);
  4. }
  5. return ParseConfigFile(path);//继续追踪
  6. }
  7. bool Parser::ParseConfigFile(const std::string& path) {
  8. LOG(INFO) << "Parsing file " << path << "...";
  9. Timer t;
  10. std::string data;
  11. if (!read_file(path, &data)) {
  12. //打开.rc文件,并返回内容
  13. return false;
  14. }
  15. data.push_back('\n'); // TODO: fix parse_config.
  16. ParseData(path, data); //核心函数,主要用来分析.rc文件内容
  17. for (const auto& sp : section_parsers_) {
  18. sp.second->EndFile(path);
  19. }
  20. LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
  21. return true;
  22. }

下面分析重点内容ParseData

  1. void Parser::ParseData(const std::string& filename, const std::string& data) {
  2. //TODO: Use a parser with const input and remove this copy
  3. std::vector<char> data_copy(data.begin(), data.end());
  4. data_copy.push_back('\0');
  5. parse_state state;
  6. state.filename = filename.c_str();
  7. state.line = 0;
  8. state.ptr = &data_copy[0];
  9. state.nexttoken = 0;
  10. SectionParser* section_parser = nullptr;
  11. std::vector<std::string> args;//存放token的容器
  12. for (;;) {
  13. //相当于词法分析器,重点代码①
  14. switch (next_token(&state)) {
  15. case T_EOF://.rc文件分析完毕
  16. if (section_parser) {
  17. section_parser->EndSection();
  18. }
  19. return;
  20. case T_NEWLINE://分析每一行的代码
  21. state.line++;
  22. if (args.empty()) {
  23. //为空
  24. break;
  25. }
  26. //section_parsers_ 匹配规则
  27. if (section_parsers_.count(args[0])) {
  28. if (section_parser) {
  29. section_parser->EndSection();
  30. }
  31. // 根据参数获取解析器②
  32. section_parser = section_parsers_[args[0]].get();
  33. std::string ret_err;
  34. // 解析内容,重点代码③
  35. if (!section_parser->ParseSection(args, &ret_err)) {
  36. parse_error(&state, "%s\n", ret_err.c_str());
  37. section_parser = nullptr;
  38. }
  39. } else if (section_parser) {
  40. std::string ret_err;
  41. if (!section_parser->ParseLineSection(args, state.filename,
  42. state.line, &ret_err)) {
  43. parse_error(&state, "%s\n", ret_err.c_str());
  44. }
  45. }
  46. args.clear();
  47. break;
  48. case T_TEXT://处理每一个token
  49. args.emplace_back(state.text);
  50. break;
  51. }
  52. }
  53. }

分析一下parse_config方法的原理。在for循环中next_token方法不断从init.rc文件中获取token。

token,就是一种编程语言的最小 单元,也就是不可再分。例如,对于传统的编程语言,if、then等关键字、变量名等标识符都属于一个token。而对于init.rc文件来 说,import、on、以及触发器的参数值,都属于一个token。

一个完整的编译器最开始需要进行词法和语法分析,词法分析就是从源代码中挑出一个个token,也就是说,词法分析器的返回值是 Token,而语法分析器的输入就是词法分析器的输出。语法分析器需要分析一个个的token,而不是一个个的字符。由于init解析语言很简 单,所以就将词法和语法分析器放到了一起。词法分析器就是next_token函数,而语法分析器就是T_NEWLINE分支中的代码,更详细的说是section_parser->ParseLineSection。

3.next_token

下面是next_token()的调用

  1. next_token(&state)

先分析传入的参数,类型是parse_state的结构体,定义在parser.h

目录:system/core/init/parser.h

  1. struct parse_state
  2. {
  3. char *ptr;
  4. char *text;
  5. int line;
  6. int nexttoken;
  7. void *context;
  8. void (*parse_line)(struct parse_state *state, int nargs, char **args);
  9. const char *filename;
  10. void *priv;
  11. };

然后继续看看next_token的实现,他的实现在parser.cpp

目录:system/core/init/parser.cpp

  1. int next_token(struct parse_state *state)
  2. {
  3. char *x = state->ptr; //需要解析的字符串
  4. char *s;
  5. if (state->nexttoken) {
  6. int t = state->nexttoken;
  7. state->nexttoken = 0;
  8. return t;
  9. }
  10. //过滤掉结束符 \r \t # 符号
  11. for (;;) {
  12. switch (*x) {
  13. case 0:
  14. state->ptr = x;
  15. return T_EOF;
  16. case '\n':
  17. x++;
  18. state->ptr = x;
  19. return T_NEWLINE;
  20. case ' ':
  21. case '\t':
  22. case '\r':
  23. x++;
  24. continue;
  25. case '#':
  26. while (*x && (*x != '\n')) x++;
  27. if (*x == '\n') {
  28. state->ptr = x+1;
  29. return T_NEWLINE;
  30. } else {
  31. state->ptr = x;
  32. return T_EOF;
  33. }
  34. default:
  35. goto text; //开始解析token
  36. }
  37. }
  38. textdone: //解析完成,返回给ParseConfig
  39. state->ptr = x; //更新下次需要解析的字符串位置
  40. *s = 0;
  41. return T_TEXT;
  42. text:
  43. state->text = s = x; //将解析出来的token存放在text中
  44. textresume:
  45. for (;;) {
  46. switch (*x) {
  47. case 0:
  48. goto textdone; //完成解析
  49. case ' ':
  50. case '\t':
  51. case '\r':
  52. x++;
  53. goto textdone;
  54. case '\n':
  55. state->nexttoken = T_NEWLINE;
  56. x++;
  57. goto textdone;
  58. case '"':
  59. x++;
  60. for (;;) {
  61. switch (*x) {
  62. case 0:
  63. /* unterminated quoted thing */
  64. state->ptr = x;
  65. return T_EOF;
  66. case '"':
  67. x++;
  68. goto textresume;
  69. default:
  70. *s++ = *x++;
  71. }
  72. }
  73. break;
  74. case '\\': //解析转义符
  75. x++;
  76. switch (*x) {
  77. case 0:
  78. goto textdone;
  79. case 'n':
  80. *s++ = '\n';
  81. break;
  82. case 'r':
  83. *s++ = '\r';
  84. break;
  85. case 't':
  86. *s++ = '\t';
  87. break;
  88. case '\\':
  89. *s++ = '\\';
  90. break;
  91. case '\r':
  92. /* \ <cr> <lf> -> line continuation */
  93. if (x[1] != '\n') {
  94. x++;
  95. continue;
  96. }
  97. case '\n':
  98. /* \ <lf> -> line continuation */
  99. state->line++;
  100. x++;
  101. /* eat any extra whitespace */
  102. while((*x == ' ') || (*x == '\t')) x++;
  103. continue;
  104. default:
  105. /* unknown escape -- just copy */
  106. *s++ = *x++;
  107. }
  108. continue;
  109. default: //读取下一个
  110. *s++ = *x++;
  111. }
  112. }
  113. return T_EOF;
  114. }

通过上面的next_token解析出来一个个tokent,接下来来就是分析token

4.section_parsers_ 获取解析器

先回顾一下ParseData的内容,现在就可解析代码含义了

目录:system/core/init/init_parser.cpp ParseData函数

  1. case T_NEWLINE: //解析出来
  2. state.line++;
  3. if (args.empty()) {
  4. break;
  5. }
  6. //使用count,返回的是被查找元素的个数。如果有,返回1;否则,返回0。
  7. //即查找该Actions/Services是否有对应的加载器
  8. if (section_parsers_.count(args[0])) {
  9. if (section_parser) {
  10. section_parser->EndSection();
  11. }
  12. //获取section_parser
  13. section_parser = section_parsers_[args[0]].get();
  14. std::string ret_err;
  15. //调用section_parser->ParseSection进行初始化 重点代码下面分析
  16. if (!section_parser->ParseSection(args, &ret_err)) {
  17. parse_error(&state, "%s\n", ret_err.c_str());
  18. section_parser = nullptr;
  19. }
  20. } else if (section_parser) {
  21. std::string ret_err;
  22. //进行解析 重点代码下面分析
  23. if (!section_parser->ParseLineSection(args, state.filename,
  24. state.line, &ret_err)) {
  25. parse_error(&state, "%s\n", ret_err.c_str());
  26. }
  27. }
  28. args.clear();
  29. break;
  30. case T_TEXT:
  31. args.emplace_back(state.text);//加入到args容器的尾部
  32. break;

获取section_parser语法分析器

ection_parser = section_parsers_[args[0]].get();获取到成员分析器

我们前面在分析Parser类说过,他有一个section_parsers_成员变量,在init.cpp通过AddSectionParser来添加对应的语法解析器。
调用

目录;system/core/init/init.cpp

  1. parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm)); //service解析器
  2. parser.AddSectionParser("on", std::make_unique<ActionParser>(&am)); //Actions解析器
  3. parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

实现

目录:system/core/init/init_parser.cpp

  1. void Parser::AddSectionParser(const std::string& name,
  2. std::unique_ptr<SectionParser> parser) {
  3. section_parsers_[name] = std::move(parser);
  4. }

我们现在主要来看看service解析器的实现

5.ServiceParser

ServiceParser的定义,在 service.h

目录: system/core/init/service.h

  1. class ServiceParser : public SectionParser {
  2. public:
  3. ServiceParser() : service_(nullptr) {
  4. }
  5. bool ParseSection(const std::vector<std::string>& args,
  6. std::string* err) override;
  7. bool ParseLineSection(const std::vector<std::string>& args,
  8. const std::string& filename, int line,
  9. std::string* err) const override;
  10. void EndSection() override;
  11. void EndFile(const std::string&) override {
  12. }
  13. private:
  14. bool IsValidName(const std::string& name) const;
  15. std::unique_ptr<Service> service_;
  16. };

重点来看ParseSection和ParseLineSection两个方法

首先看看ParseSection方法

目录:system/core/init/service.c

  1. bool ServiceParser::ParseSection(const std::vector<std::string>& args,
  2. std::string* err) {
  3. if (args.size() < 3) {
  4. *err = "services must have a name and a program";
  5. return false;
  6. }
  7. const std::string& name = args[1];
  8. if (!IsValidName(name)) {
  9. *err = StringPrintf("invalid service name '%s'", name.c_str());
  10. return false;
  11. }
  12. std::vector<std::string> str_args(args.begin() + 2, args.end());//获取Service的参数
  13. service_ = std::make_unique<Service>(name, str_args);//创建对应的Service类
  14. return true;
  15. }
  16. //Service的构造方法
  17. Service::Service(const std::string& name, const std::vector<std::string>& args)
  18. : name_(name), //确定service的名字
  19. classnames_({
  20. "default"}),
  21. flags_(0),
  22. pid_(0),
  23. crash_count_(0),
  24. uid_(0),
  25. gid_(0),
  26. namespace_flags_(0),
  27. seclabel_(""),
  28. keychord_id_(0),
  29. ioprio_class_(IoSchedClass_NONE),
  30. ioprio_pri_(0),
  31. priority_(0),
  32. oom_score_adjust_(-1000),
  33. args_(args) {
  34. onrestart_.InitSingleTrigger("onrestart"); //设置重启
  35. }

再来分析ParseLineSection

目录:system/core/init/service.c

  1. bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
  2. const std::string& filename, int line,
  3. std::string* err) const {
  4. return service_ ? service_->ParseLine(args, err) : false; //
  5. }
  6. bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
  7. if (args.empty()) {
  8. *err = "option needed, but not provided";
  9. return false;
  10. }
  11. //查看OptionParserMap的构造函数
  12. static const OptionParserMap parser_map;
  13. //找出该function对应的方法
  14. auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);
  15. if (!parser) {
  16. return false;
  17. }
  18. //执行该方法
  19. return (this->*parser)(args, err);
  20. }
  21. //OptionParserMap的构造函数
  22. class Service::OptionParserMap : public KeywordMap<OptionParser> {
  23. public:
  24. OptionParserMap() {
  25. }
  26. private:
  27. Map& map() const override;
  28. };
  29. Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
  30. constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
  31. // clang-format off
  32. //将对应的字段转换成对应的方面
  33. static const Map option_parsers = {
  34. {
  35. "capabilities",
  36. {
  37. 1, kMax, &Service::ParseCapabilities}},
  38. {
  39. "class", {
  40. 1, kMax, &Service::ParseClass}},
  41. {
  42. "console", {
  43. 0, 1, &Service::ParseConsole}},
  44. {
  45. "critical", {
  46. 0, 0, &Service::ParseCritical}},
  47. {
  48. "disabled", {
  49. 0, 0, &Service::ParseDisabled}},
  50. {
  51. "group", {
  52. 1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
  53. {
  54. "ioprio", {
  55. 2, 2, &Service::ParseIoprio}},
  56. {
  57. "priority", {
  58. 1, 1, &Service::ParsePriority}},
  59. {
  60. "keycodes", {
  61. 1, kMax, &Service::ParseKeycodes}},
  62. {
  63. "oneshot", {
  64. 0, 0, &Service::ParseOneshot}},
  65. {
  66. "onrestart", {
  67. 1, kMax, &Service::ParseOnrestart}},
  68. {
  69. "oom_score_adjust",
  70. {
  71. 1, 1, &Service::ParseOomScoreAdjust}},
  72. {
  73. "namespace", {
  74. 1, 2, &Service::ParseNamespace}},
  75. {
  76. "seclabel", {
  77. 1, 1, &Service::ParseSeclabel}},
  78. {
  79. "setenv", {
  80. 2, 2, &Service::ParseSetenv}},
  81. {
  82. "socket", {
  83. 3, 6, &Service::ParseSocket}},
  84. {
  85. "file", {
  86. 2, 2, &Service::ParseFile}},
  87. {
  88. "user", {
  89. 1, 1, &Service::ParseUser}},
  90. {
  91. "writepid", {
  92. 1, kMax, &Service::ParseWritepid}},
  93. };
  94. // clang-format on
  95. return option_parsers;
  96. }

在解析完所有Service后,会调用EndSection方法

  1. void ServiceParser::EndSection() {
  2. if (service_) {
  3. ServiceManager::GetInstance().AddService(std::move(service_));
  4. }
  5. }
  6. void ServiceManager::AddService(std::unique_ptr<Service> service) {
  7. Service* old_service = FindServiceByName(service->name());
  8. if (old_service) {
  9. LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
  10. return;
  11. }
  12. services_.emplace_back(std::move(service)); //将Service加入到Service链表中,并根据之前的即内容进行填充
  13. }

五.小结

  • 在init进程中将rc文件传入,并传入对应的解析器
  • 在init_parser利用parser对rc文件进行解析
  • 解析分为两个步骤:词法分析和语法分析
  • 词法分析,将rc文件里的字符分割成一个个对应的token
  • 语法分析,将一个个token放容器中,调用对应的解析器创建Service/Actions,然后根据对应的token在map中映射为对于的方法,进行调用。

六.参考资料

Android的init过程(二):初始化语言(init.rc)解析
Android P on property属性无法trigger流程分析

发表评论

表情:
评论列表 (有 0 条评论,530人围观)

还没有评论,来说两句吧...

相关阅读