在解析Init.rc之前需要对init.rc文件有一个认识,init.rc是由一种被称为"Android初始化语言"(Android Init Language,这里简称为AIL)的脚本写成的文件.该语言是由语句组成的,主要包含了五种类型的语句:
- Action
- Commands
- Services
- Options
- Import
在init.rc文件中一条语句通常占用一行,单词之间是用空格符来相隔的。如果一行写不下,可以在行尾加上反斜杠,来连接下一行。也就是说,可以用反斜杠将多行代码连接成一行代码。并且使用#来进行注释。在init.rc中分成三个部分(Section),而每一部分的开头需要指定on(Actions)、service(Services)或import。也就是说,每一个Actions, import或 Services确定一个Section。而所有的Commands和Options只能属于最近定义的Section。如果Commands和 Options在第一个Section之前被定义,它们将被忽略。Actions和Services的名称必须唯一。如果有两个或多个Actions或Services拥有同样的名称,那么init在执行它们时将抛出错误,并忽略这些Action和Service。
我们在之前分析init进程时讲到过在init.cpp的main函数中将service, on, import设置为三个Section.
- Parser& parser = Parser::GetInstance();
- parser.AddSectionParser("service",std::make_unique<ServiceParser>());
- parser.AddSectionParser("on", std::make_unique<ActionParser>());
- parser.AddSectionParser("import", std::make_unique<ImportParser>());
在system/core/init/readme.txt中详细说明
Action的格式如下:
- on <trigger> [&& <trigger>]* //设置触发器
- <command>
- <command> //动作触发之后要执行的命令
- <command>
- Triggers //对trigger的详细讲解
- --------
- Triggers are strings which can be used to match certain kinds of
- events and used to cause an action to occur.
- Triggers are subdivided into event triggers and property triggers.
- Event triggers are strings triggered by the 'trigger' command or by
- the QueueEventTrigger() function within the init executable. These
- take the form of a simple string such as 'boot' or 'late-init'.
- Property triggers are strings triggered when a named property changes
- value to a given new value or when a named property changes value to
- any new value. These take the form of 'property:<name>=<value>' and
- 'property:<name>=*' respectively. Property triggers are additionally
- evaluated and triggered accordingly during the initial boot phase of
- init.
- An Action can have multiple property triggers but may only have one
- event trigger.
- For example:
- 'on boot && property:a=b' defines an action that is only executed when
- the 'boot' event trigger happens and the property a equals b.
- 'on property:a=b && property:c=d' defines an action that is executed
- at three times,
- 1) During initial boot if property a=b and property c=d
- 2) Any time that property a transitions to value b, while property
- c already equals d.
- 3) Any time that property c transitions to value d, while property
- a already equals b.
在init.cpp中设置的trigger有early-init, init, late-init等, 当trigger被触发时就执行command,
我们来看一个标准的Action:
- on early-init //trigger为early-init,在init.cpp的main函数中设置过
- # Set init and its forked children's oom_adj.
- write /proc/1/oom_score_adj -1000 //调用do_write函数, 写入oom_score_adj为-1000
- # Disable sysrq from keyboard
- write /proc/sys/kernel/sysrq 0
- # Set the security context of /adb_keys if present.
- restorecon /adb_keys //为adb_keys 重置安全上下文
- # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
- mkdir /mnt 0775 root system //创建mnt目录
- # Set the security context of /postinstall if present.
- restorecon /postinstall
- start ueventd //调用函数do_start, 启动服务uevent,
下面对所有命令详细讲解.
- bootchart_init //初始化bootchart,用于获取开机过程系统信息
- Start bootcharting if configured (see below).
- This is included in the default init.rc.
- chmod <octal-mode> <path> //改变文件的权限
- Change file access permissions.
- chown <owner> <group> <path> //改变文件的群组
- Change file owner and group.
- class_start <serviceclass> //启动所有具有特定class的services
- Start all services of the specified class if they are
- not already running.
- class_stop <serviceclass> //将具有特定class的所有运行中的services给停止或者diasble
- Stop and disable all services of the specified class if they are
- currently running.
- class_reset <serviceclass> //先将services stop掉, 之后可能会通过class_start再重新启动起来
- Stop all services of the specified class if they are
- currently running, without disabling them. They can be restarted
- later using class_start.
- copy <src> <dst> //复制文件
- Copies a file. Similar to write, but useful for binary/large
- amounts of data.
- domainname <name>
- Set the domain name.
- enable <servicename> //如果services没有特定disable,就将他设为enable
- Turns a disabled service into an enabled one as if the service did not
- specify disabled.
- If the service is supposed to be running, it will be started now.
- Typically used when the bootloader sets a variable that indicates a specific
- service should be started when needed. E.g.
- on property:ro.boot.myfancyhardware=1
- enable my_fancy_service_for_my_fancy_hardware
- exec [ <seclabel> [ <user> [ <group> ]* ] ] -- <command> [ <argument> ]* //创建执行程序.比较重要,后面启动service要用到
- Fork and execute command with the given arguments. The command starts
- after "--" so that an optional security context, user, and supplementary
- groups can be provided. No other commands will be run until this one
- export <name> <value> //在全局设置环境变量
- Set the environment variable <name> equal to <value> in the
- global environment (which will be inherited by all processes
- started after this command is executed)
- hostname <name> //设置主机名称
- Set the host name.
- ifup <interface> //启动网络接口
- Bring the network interface <interface> online.
- insmod <path> //在某个路径安装一个模块
- Install the module at <path>
- load_all_props //加载所有的配置
- Loads properties from /system, /vendor, et cetera.
- This is included in the default init.rc.
- load_persist_props //当data加密时加载一些配置
- Loads persistent properties when /data has been decrypted.
- This is included in the default init.rc.
- loglevel <level> //设置kernel log level
- Sets the kernel log level to level. Properties are expanded within <level>.
- mkdir <path> [mode] [owner] [group] //创建文件夹
- Create a directory at <path>, optionally with the given mode, owner, and
- group. If not provided, the directory is created with permissions 755 and
- owned by the root user and root group. If provided, the mode, owner and group
- will be updated if the directory exists already.
- mount_all <fstab> [ <path> ]* //挂载
- Calls fs_mgr_mount_all on the given fs_mgr-format fstab and imports .rc files
- at the specified paths (e.g., on the partitions just mounted). Refer to the
- section of "Init .rc Files" for detail.
- mount <type> <device> <dir> [ <flag> ]* [<options>] //在dir文件夹下面挂载设备
- Attempt to mount the named device at the directory <dir>
- <device> may be of the form mtd@name to specify a mtd block
- device by name.
- <flag>s include "ro", "rw", "remount", "noatime", ...
- <options> include "barrier=1", "noauto_da_alloc", "discard", ... as
- a comma separated string, eg: barrier=1,noauto_da_alloc
- powerctl
- Internal implementation detail used to respond to changes to the
- "sys.powerctl" system property, used to implement rebooting.
- restart <service> //重启服务
- Like stop, but doesn't disable the service.
- restorecon <path> [ <path> ]* //重置文件的安全上下文
- Restore the file named by <path> to the security context specified
- in the file_contexts configuration.
- Not required for directories created by the init.rc as these are
- automatically labeled correctly by init.
- restorecon_recursive <path> [ <path> ]*//一般都是 selinux完成初始化之后又创建、或者改变的目录
- Recursively restore the directory tree named by <path> to the
- security contexts specified in the file_contexts configuration.
- rm <path> //删除文件
- Calls unlink(2) on the given path. You might want to
- use "exec -- rm ..." instead (provided the system partition is
- already mounted).
- rmdir <path> //删除文件夹
- Calls rmdir(2) on the given path.
- setprop <name> <value> //设置属性
- Set system property <name> to <value>. Properties are expanded
- within <value>.
- setrlimit <resource> <cur> <max>
- Set the rlimit for a resource.
- start <service> //如果service没有启动,就将他启动起来
- Start a service running if it is not already running.
- stop <service> //将运行的服务停掉
- Stop a service from running if it is currently running.
- swapon_all <fstab>
- Calls fs_mgr_swapon_all on the given fstab file.
- symlink <target> <path>
- Create a symbolic link at <path> with the value <target>
- sysclktz <mins_west_of_gmt>
- Set the system clock base (0 if system clock ticks in GMT)
- trigger <event> //触发一个事件
- Trigger an event. Used to queue an action from another
- action.
- verity_load_state
- Internal implementation detail used to load dm-verity state.
- verity_update_state <mount_point>
- Internal implementation detail used to update dm-verity state and
- set the partition.<mount_point>.verified properties used by adb remount
- because fs_mgr can't set them directly itself.
- wait <path> [ <timeout> ] //等待
- Poll for the existence of the given file and return when found,
- or the timeout has been reached. If timeout is not specified it
- currently defaults to five seconds.
- write <path> <content> //写文件
- Open the file at <path> and write a string to it with write(2).
- If the file does not exist, it will be created. If it does exist,
- it will be truncated. Properties are expanded within <content>.
这些命令在执行时会通过在BuiltinFunctionMap中的对应关系找到自己对应的函数.
Services格式如下:
- service <name> <pathname> [ <argument> ]* //service的名字,启动路径,以及参数
- <option> //option影响什么时候,如何启动services
- <option>
- ...
- Options //所有的options命令如下
- -------
- Options are modifiers to services. They affect how and when init
- runs the service.
- critical //表示该service非常重要,如果退出四次以上在4分钟内,设备就会重启进入recovery模式
- This is a device-critical service. If it exits more than four times in
- four minutes, the device will reboot into recovery mode.
- disabled //该services不能通过class启动,只能通过name将他启动
- This service will not automatically start with its class.
- It must be explicitly started by name.
- setenv <name> <value> //设置环境变量
- Set the environment variable <name> to <value> in the launched process.
- socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
- Create a unix domain socket named /dev/socket/<name> and pass
- its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket".
- User and group default to 0.
- 'seclabel' is the SELinux security context for the socket.
- It defaults to the service security context, as specified by seclabel or
- computed based on the service executable file security context.
- user <username>
- Change to username before exec'ing this service.
- Currently defaults to root. (??? probably should default to nobody)
- As of Android M, processes should use this option even if they
- require linux capabilities. Previously, to acquire linux
- capabilities, a process would need to run as root, request the
- capabilities, then drop to its desired uid. There is a new
- mechanism through fs_config that allows device manufacturers to add
- linux capabilities to specific binaries on a file system that should
- be used instead. This mechanism is described on
- http://source.android.com/devices/tech/config/filesystem.html. When
- using this new mechanism, processes can use the user option to
- select their desired uid without ever running as root.
- group <groupname> [ <groupname> ]*
- Change to groupname before exec'ing this service. Additional
- groupnames beyond the (required) first one are used to set the
- supplemental groups of the process (via setgroups()).
- Currently defaults to root. (??? probably should default to nobody)
- seclabel <seclabel>
- Change to 'seclabel' before exec'ing this service.
- Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
- Services on the system partition can instead use policy-defined transitions
- based on their file security context.
- If not specified and no transition is defined in policy, defaults to the init context.
- oneshot
- Do not restart the service when it exits.
- class <name> //一个特定的name, 所有有这个name的service统一管理,一起启动,一起stop
- Specify a class name for the service. All services in a
- named class may be started or stopped together. A service
- is in the class "default" if one is not specified via the
- class option.
- onrestart //当服务重启时执行
- Execute a Command (see below) when service restarts.
- writepid <file...>
- Write the child's pid to the given files when it forks. Meant for
- cgroup/cpuset usage.
service的名字为zygote, 启动路径为手机中/system/bin/app_process64后面的都是启动参数.
- service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
- class main //zygote的name为main,和class name为main的一块被启动
- socket zygote stream 660 root system //为zygote创建socket
- onrestart write /sys/android_power/request_state wake //当zygote重启时执行下面动作
- onrestart write /sys/power/state on
- onrestart restart audioserver
- onrestart restart cameraserver
- onrestart restart media
- onrestart restart netd
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
在分析init进程时知道解析init.rc文件的入口在init.cpp的main函数中
- parser.ParseConfig("/init.rc");
- bool Parser::ParseConfig(const std::string& path) {
- if (is_dir(path.c_str())) { //如果路径为文件夹,就调用解析文件夹的函数
- return ParseConfigDir(path);
- }
- return ParseConfigFile(path); //解析init.rc文件
- }
- bool Parser::ParseConfigFile(const std::string& path) {
- INFO("Parsing file %s...\n", path.c_str()); //根据之前讲解klog一文,可知该log打印不出
- Timer t; //利用Timer计时,解析文件耗时多长时间
- std::string data;
- if (!read_file(path.c_str(), &data)) { //读取文件
- return false;
- }
- data.push_back('\n'); // TODO: fix parse_config.
- ParseData(path, data); //解析内容
- for (const auto& sp : section_parsers_) {
- sp.second->EndFile(path); //解析完init.rc文件后, 调用Import_parse.cpp的EndFile函数,解析引用的rc文件
- }
- // Turning this on and letting the INFO logging be discarded adds 0.2s to
- // Nexus 9 boot time, so it's disabled by default.
- if (false) DumpState();
- NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration()); //打印出解析哪个文件,花费多长时间, 来查找耗时点
- return true;
- }
- void Parser::ParseData(const std::string& filename, const std::string& data) {
- //TODO: Use a parser with const input and remove this copy
- std::vector<char> data_copy(data.begin(), data.end()); //数据copy
- data_copy.push_back('\0');
- parse_state state;
- state.filename = filename.c_str();
- state.line = 0;
- state.ptr = &data_copy[0];
- state.nexttoken = 0;
- SectionParser* section_parser = nullptr;
- std::vector<std::string> args;
- for (;;) { //循环解析init.rc文件
- switch (next_token(&state)) { //通过nextToken函数获得需要解析的这一行是文本内容还是action,services
- case T_EOF: <span style="font-family:Arial, Helvetica, sans-serif;">//文件解析结束</span>
- if (section_parser) {
- section_parser->EndSection();
- }
- return;
- case T_NEWLINE: //解析新的一行, 可能是一个action,services或者import
- state.line++;
- if (args.empty()) {
- break;
- }
- if (section_parsers_.count(args[0])) {
- if (section_parser) {
- section_parser->EndSection(); //section解析结束
- }
- section_parser = section_parsers_[args[0]].get();
- std::string ret_err;
- if (!section_parser->ParseSection(args, &ret_err)) { //解析Action,Service, Import 三个Section
- parse_error(&state, "%s\n", ret_err.c_str());
- section_parser = nullptr;
- }
- } else if (section_parser) {
- std::string ret_err; //解析section的内容
- if (!section_parser->ParseLineSection(args, state.filename,
- state.line, &ret_err)) {
- parse_error(&state, "%s\n", ret_err.c_str());
- }
- }
- args.clear();
- break;
- case T_TEXT:
- args.emplace_back(state.text); //将文本放入args中
- break;
- }
- }
- }
nextToken函数在/system/core/init/parse.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++;
- switch (*x) {
- case 0:
- goto textdone;
- case 'n':
- *s++ = '\n';
- break;
- case 'r':
- *s++ = '\r';
- break;
- case 't':
- *s++ = '\t';
- break;
- case '\\':
- *s++ = '\\';
- break;
- case '\r':
- /* \ <cr> <lf> -> line continuation */
- if (x[1] != '\n') {
- x++;
- continue;
- }
- case '\n':
- /* \ <lf> -> line continuation */
- state->line++;
- x++;
- /* eat any extra whitespace */
- while((*x == ' ') || (*x == '\t')) x++;
- continue;
- default:
- /* unknown escape -- just copy */
- *s++ = *x++;
- }
- continue;
- default:
- *s++ = *x++;
- }
- }
- return T_EOF;
解析Action
调用ActionParser的ParseSection函数,代码位置system/core/init/action.cpp
- bool ActionParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
- std::vector<std::string> triggers(args.begin() + 1, args.end()); //获取trigger, 截取on后面的字符串
- if (triggers.size() < 1) {
- *err = "actions must have a trigger"; //检查是否存在trigger
- return false;
- }
- auto action = std::make_unique<Action>(false);
- if (!action->InitTriggers(triggers, err)) { //将triggers放入event_trigger_
- return false;
- }
- action_ = std::move(action); //赋值action_
- return true;
- }
- bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
- return action_ ? action_->AddCommand(args, filename, line, err) : false; //action_为true ,已经赋过值
- }
- bool Action::AddCommand(const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err) {
- if (!function_map_) { //function_map在分析init进程时有提到, 为true
- *err = "no function map available";
- return false;
- }
- if (args.empty()) {
- *err = "command needed, but not provided";
- return false;
- }
- auto function = function_map_->FindFunction(args[0], args.size() - 1, err); //根据命令找到对应的函数
- if (!function) {
- return false;
- }
- AddCommand(function, args, filename, line);
- return true;
- }
- void Action::AddCommand(BuiltinFunction f,
- const std::vector<std::string>& args,
- const std::string& filename, int line) {
- commands_.emplace_back(f, args, filename, line); //将对应函数, 参数,文件名放入commans_中
- }
- void ActionParser::EndSection() {
- if (action_ && action_->NumCommands() > 0) {
- ActionManager::GetInstance().AddAction(std::move(action_)); //将一个action的所有command都加入action_后,将action_加入action列表
- }
- }
- void ActionManager::AddAction(std::unique_ptr<Action> action) {
- auto old_action_it =
- std::find_if(actions_.begin(), actions_.end(),
- [&action] (std::unique_ptr<Action>& a) {
- return action->TriggersEqual(*a);
- });
- if (old_action_it != actions_.end()) {
- (*old_action_it)->CombineAction(*action);
- } else {
- actions_.emplace_back(std::move(action)); //将所有的action加入actions_列表
- }
- }
调用ServiceParser的ParseSection函数,代码位置system/core/init/service.cpp
- bool ServiceParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
- if (args.size() < 3) { //判断service是否有name与可执行程序
- *err = "services must have a name and a program";
- return false;
- }
- const std::string& name = args[1];
- if (!IsValidName(name)) { //检查name是否可用
- *err = StringPrintf("invalid service name '%s'", name.c_str());
- return false;
- }
- std::vector<std::string> str_args(args.begin() + 2, args.end()); //获取执行程序与参数
- service_ = std::make_unique<Service>(name, "default", str_args); //给service_赋值
- return true;
- }
- bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
- return service_ ? service_->HandleLine(args, err) : false; //service_为true, 调用HandleLine
- }
- Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
- constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
- static const Map option_handlers = { //option对应的函数
- {"class", {1, 1, &Service::HandleClass}},
- {"console", {0, 0, &Service::HandleConsole}},
- {"critical", {0, 0, &Service::HandleCritical}},
- {"disabled", {0, 0, &Service::HandleDisabled}},
- {"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
- {"ioprio", {2, 2, &Service::HandleIoprio}},
- {"keycodes", {1, kMax, &Service::HandleKeycodes}},
- {"oneshot", {0, 0, &Service::HandleOneshot}},
- {"onrestart", {1, kMax, &Service::HandleOnrestart}},
- {"seclabel", {1, 1, &Service::HandleSeclabel}},
- {"setenv", {2, 2, &Service::HandleSetenv}},
- {"socket", {3, 6, &Service::HandleSocket}},
- {"user", {1, 1, &Service::HandleUser}},
- {"writepid", {1, kMax, &Service::HandleWritepid}},
- };
- return option_handlers;
- }
- bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
- if (args.empty()) {
- *err = "option needed, but not provided";
- return false;
- }
- static const OptionHandlerMap handler_map; //获得option对应的函数表
- auto handler = handler_map.FindFunction(args[0], args.size() - 1, err); //根据option获取对应的函数名
- if (!handler) {
- return false;
- }
- return (this->*handler)(args, err);
- }
- void ServiceParser::EndSection() {
- if (service_) {
- ServiceManager::GetInstance().AddService(std::move(service_));
- }
- }
- void ServiceManager::AddService(std::unique_ptr<Service> service) {
- Service* old_service = FindServiceByName(service->name());
- if (old_service) { //service已经被定义过了就抛弃
- ERROR("ignored duplicate definition of service '%s'",
- service->name().c_str());
- return;
- }
- services_.emplace_back(std::move(service)); //将service添加services_列表
- }
调用ImportParser的ParseSection函数,代码位置system/core/init/import_parser.cpp
- bool ImportParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
- if (args.size() != 2) {
- *err = "single argument needed for import\n";
- return false;
- }
- std::string conf_file;
- bool ret = expand_props(args[1], &conf_file); //获取引用的conf_file文件,
- if (!ret) {
- *err = "error while expanding import";
- return false;
- }
- INFO("Added '%s' to import list\n", conf_file.c_str());
- imports_.emplace_back(std::move(conf_file)); //将所有的conf_file添加到imports_列表
- return true;
- }
- void ImportParser::EndFile(const std::string& filename) {
- auto current_imports = std::move(imports_); //获取imports_
- imports_.clear(); //将imports_列表清空
- for (const auto& s : current_imports) { //遍历列表
- if (!Parser::GetInstance().ParseConfig(s)) { //调用ParseConfig函数,对其他配置进行解析, 流程遇上面的相同
- ERROR("could not import file '%s' from '%s': %s\n",
- s.c_str(), filename.c_str(), strerror(errno));
- }
- }
- }
到此init.rc文件就解析完了, 那么大家就想知道了解析完的actions与services又是如何执行的呢?