当前位置:   article > 正文

(连载)Android 8.0 : 系统启动流程之init进程(三)

tcp_def_init_rwnd

这是一个连载的博文系列,我将持续为大家提供尽可能透彻的Android源码分析 github连载地址

前言

init经过前两个阶段后,已经建立了属性系统和SELinux系统,但是init进程还需要执行很多其他的操作,还要启动许多关键的系统服务, 但是如果都是像属性系统和SELinux系统那样一行行代码去做,显得有点杂乱繁琐,而且不容易扩展,所以Android系统引入了init.rc

init.rc是init进程启动的配置脚本,这个脚本是用一种叫Android Init Language(Android初始化语言)的语言写的, 在7.0以前,init进程只解析根目录下的init.rc文件,但是随着版本的迭代,init.rc越来越臃肿, 所以在7.0以后,init.rc一些业务被分拆到/system/etc/init,/vendor/etc/init,/odm/etc/init三个目录下, 在本篇文章中,我将讲解init.rc的一些语法,然后一步步分析init进程是如何去解析init.rc文件的

本文主要讲解以下内容

  • Android Init Language语法
  • 解析.rc文件
  • 加入一些事件和一些Action
  • 触发所有事件并不断监听新的事件

本文涉及到的文件

  1. platform/system/core/init/README.md
  2. platform/system/core/init/init.cpp
  3. platform/system/core/init/init_parser.cpp
  4. platform/system/core/init/action.cpp
  5. platform/system/core/init/action.h
  6. platform/system/core/init/keyword_map.h
  7. platform/system/core/init/builtins.cpp
  8. platform/system/core/init/service.cpp
  9. platform/system/core/init/service.h
  10. platform/system/core/init/import_parser.cpp
  11. platform/system/core/init/util.cpp
  12. 复制代码

一、Android Init Language语法

定义在platform/system/core/init/README.md

.rc文件主要配置了两个东西,一个是action,一个是service,trigger和command是对action的补充,options是对service的补充. action加上trigger以及一些command,组成一个Section,service加上一些option,也组成一个Section ,.rc文件就是由一个个Section组成. .rc文件头部有一个import的语法,表示这些.rc也一并包含并解析,接下来我们重点讲下action和service.

action的格式如下:

  1. on <trigger> [&& <trigger>]*
  2. <command>
  3. <command>
  4. <command>
  5. 复制代码

以on开头,trigger是判断条件,command是具体执行一些操作,当满足trigger条件时,执行这些command
trigger可以是一个字符串,如

  1. on early //表示当trigger early或QueueEventTrigger("early")调用时触发
  2. 复制代码

也可以是属性,如

  1. on property:sys.boot_from_charger_mode=1//表示当sys.boot_from_charger_mode的值通过property_set设置为1时触发
  2. on property:sys.sysctl.tcp_def_init_rwnd=* // *表示任意值
  3. 复制代码

条件可以是多个,用&&连接,如

  1. on zygote-start && property:ro.crypto.state=unencrypted
  2. //表示当zygote-start触发并且ro.crypto.state属性值为unencrypted时触发
  3. 复制代码

command就是一些具体的操作,如

  1. mkdir /dev/fscklogs 0770 root system //新建目录
  2. class_stop charger //终止服务
  3. trigger late-init //触发late-init
  4. 复制代码

services的格式如下:

  1. service <name> <pathname> [ <argument> ]*
  2. <option>
  3. <option>
  4. ...
  5. 复制代码

以service开头,name是指定这个服务的名称,pathname表示这个服务的执行文件路径,argument表示执行文件带的参数,option表示这个服务的一些配置 我们看一个典型的例子就知道了

  1. service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
  2. class main
  3. priority -20
  4. user root
  5. group root readproc
  6. socket zygote stream 660 root system
  7. onrestart write /sys/android_power/request_state wake
  8. onrestart write /sys/power/state on
  9. onrestart restart audioserver
  10. onrestart restart cameraserver
  11. onrestart restart media
  12. onrestart restart netd
  13. onrestart restart wificond
  14. writepid /dev/cpuset/foreground/tasks
  15. 复制代码

这个是配置在 /init.zygote64_32.rc文件中的service, 它就是我们常说的zygote进程的启动配置

zygote是进程名,可执行文件路径在/system/bin/app_process64,执行文件参数(就是可执行程序main函数里面的那个args)是 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote

后面的option是一些服务配置,比如 class main表示所属class是main,相当于一个归类,其他service也可以归为main,他们会被一起启动或终止, service有一个name,也有一个class,就像工作中,你有一个名字叫foxleezh,也可以说你属于android部门.

我上面说的这些东西,源码中已经有一个专门的文档用来说明,路径在platform/system/core/init/README.md,应当说这个文档写得还是挺不错的,认真读这个文档的话,基本的语法知识就都知道了,我简单翻译下

Android Init Language

Android Init Language中由5类语法组成,分别是Actions, Commands, Services, Options, and Imports

每一行是一个语句,单词之间用空格分开,如果单词中有空格可以用反斜杠转义,也可以用双引号来引用文本避免和空格冲突,如果一行语句太长可以用 \ 换行,用 # 表示注释

Actions和Services可以作为一个独立的Section,所有的Commands和Options从属于紧挨着的Actions或Services,定义在第一个Section前的Commands和Options将被忽略掉

Actions和Services都是唯一的,如果定义了两个一样的Action,第二个Action的Command将追加到第一个Action, 如果定义了两个一样的Service,第二个Service将被忽略掉并打印错误日志

Init .rc Files

Android Init Language是用后缀为.rc的纯文本编写的,而且是由多个分布在不同目录下的.rc文件组成,如下所述

/init.rc 是最主要的一个.rc文件,它由init进程在初始化时加载,主要负责系统初始化,它会导入 /init.${ro.hardware}.rc ,这个是系统级核心厂商提供的主要.rc文件

当执行 mount_all 语句时,init进程将加载所有在 /{system,vendor,odm}/etc/init/ 目录下的文件,挂载好文件系统后,这些目录将会为Actions和Services服务

有一个特殊的目录可能被用来替换上面的三个默认目录,这主要是为了支持工厂模式和其他非标准的启动模式,上面三个目录用于正常的启动过程

这三个用于扩展的目录是

  1. /system/etc/init/ 用于系统本身,比如SurfaceFlinger, MediaService, and logcatd.
  2. /vendor/etc/init/ 用于SoC(系统级核心厂商,如高通),为他们提供一些核心功能和服务
  3. /odm/etc/init/ 用于设备制造商(odm定制厂商,如华为、小米),为他们的传感器或外围设备提供一些核心功能和服务

所有放在这三个目录下的Services二进制文件都必须有一个对应的.rc文件放在该目录下,并且要在.rc文件中定义service结构, 有一个宏LOCAL_INIT_RC,可以帮助开发者处理这个问题. 每个.rc文件还应当包含一些与该服务相关的actions

举个例子,在system/core/logcat目录下有logcatd.rc和Android.mk这两个文件. Android.mk文件中用LOCAL_INIT_RC这个宏,在编译时将logcatd.rc放在/system/etc/init/目录下,init进程在调用 mount_all 时将其加载,在合适的时机运行其定义的service并将action放入队列

将init.rc根据不同服务分拆到不同目录,要比之前放在单个init.rc文件好. 这种方案确保init读取的service和action信息能和同目录下的Services二进制文件更加符合,不再像以前单个init.rc那样. 另外,这样还可以解决多个services加入到系统时发生的冲突,因为他们都拆分到了不同的文件中

在 mount_all 语句中有 "early" 和 "late" 两个可选项,当 early 设置的时候,init进程将跳过被 latemount 标记的挂载操作,并触发fs encryption state 事件, 当 late 被设置的时候,init进程只会执行 latemount 标记的挂载操作,但是会跳过导入的 .rc文件的执行. 默认情况下,不设置任何选项,init进程将执行所有挂载操作

Actions

Actions由一行行命令组成. trigger用来决定什么时候触发这些命令,当一个事件满足trigger的触发条件时, 这个action就会被加入到处理队列中(除非队列中已经存在)

队列中的action按顺序取出执行,action中的命令按顺序执行. 这些命令主要用来执行一些操作(设备创建/销毁,属性设置,进程重启)

Actions的格式如下:

  1. on <trigger> [&& <trigger>]*
  2. <command>
  3. <command>
  4. <command>
  5. 复制代码

Services

Services是init进程启动的程序,它们也可能在退出时自动重启. Services的格式如下:

  1. service <name> <pathname> [ <argument> ]*
  2. <option>
  3. <option>
  4. ...
  5. 复制代码

Options

Options是Services的参数配置. 它们影响Service如何运行及运行时机

console [<console>]
Service需要控制台. 第二个参数console的意思是可以设置你想要的控制台类型,默认控制台是/dev/console ,/dev 这个前缀通常是被忽略的,比如你要设置控制台 /dev/tty0 ,那么只需要设置为console tty0

critical
表示Service是严格模式. 如果这个Service在4分钟内退出超过4次,那么设备将重启进入recovery模式

disabled
表示Service不能以class的形式启动,只能以name的形式启动

setenv <name> <value>
在Service启动时设置name-value的环境变量

socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
创建一个unix域的socket,名字叫/dev/socket/name , 并将fd返回给Service. type 只能是 "dgram", "stream" or "seqpacket". User 和 group 默认值是 0. 'seclabel' 是这个socket的SELinux安全上下文,它的默认值是service安全策略或者基于其可执行文件的安全上下文. 它对应的本地实现在libcutils的android_get_control_socket

file <path> <type>
打开一个文件,并将fd返回给这个Service. type 只能是 "r", "w" or "rw". 它对应的本地实现在libcutils的android_get_control_file

user <username>
在启动Service前将user改为username,默认启动时user为root(或许默认是无). 在Android M版本,如果一个进程想拥有Linux capabilities(相当于Android中的权限吧),也只能通过设置这个值. 以前,一个程序要想有Linux capabilities,必须先以root身份运行,然后再降级到所需的uid. 现在已经有一套新的机制取而代之,它通过fs_config允许厂商赋予特殊二进制文件Linux capabilities. 这套机制的说明文档在source.android.com/devices/tec…. 当使用这套新的机制时,程序可以通过user参数选择自己所需的uid,而不需要以root权限运行. 在Android O版本, 程序可以通过capabilities参数直接申请所需的能力,参见下面的capabilities说明

group <groupname> [ <groupname>\* ]
在启动Service前将group改为第一个groupname,第一个groupname是必须有的, 默认值为root(或许默认值是无),第二个groupname可以不设置,用于追加组(通过setgroups).

capabilities <capability> [ <capability>\* ]
在启动Service时将capabilities设置为capability. 'capability' 不能是"CAP_" prefix, like "NET_ADMIN" or "SETPCAP". 参考 http://man7.org/linux/man-pages/man7/capabilities.7.html ,里面有capability的说明.

seclabel <seclabel>
在启动Service前将seclabel设置为seclabel. 主要用于在rootfs上启动的service,比如ueventd, adbd. 在系统分区上运行的service有自己的SELinux安全策略,如果不设置,默认使用init的安全策略.

oneshot
退出后不再重启

class <name> [ <name>\* ]
为Service指定class名字. 同一个class名字的Service会被一起启动或退出,默认值是"default",第二个name可以不设置,用于service组.

animation class
animation class 主要包含为开机动画或关机动画服务的service. 它们很早被启动,而且直到关机最后一步才退出. 它们不允许访问/data 目录,它们可以检查/data目录,但是不能打开 /data 目录,而且需要在 /data 不能用时也正常工作 .

onrestart
在Service重启时执行命令.

writepid <file> [ <file>\* ]
当Service调用fork时将子进程的pid写入到指定文件. 用于cgroup/cpuset的使用,当/dev/cpuset/下面没有文件但ro.cpuset.default的值却不为空时, 将pid的值写入到/dev/cpuset/cpuset_name/tasks文件中

priority <priority>
设置进程优先级. 在-20~19之间,默认值是0,能过setpriority实现

namespace <pid|mnt>
当fork这个service时,设置pid或mnt标记

oom_score_adjust <value>
设置子进程的 /proc/self/oom_score_adj 的值为 value,在 -1000 ~ 1000之间.

Triggers

Triggers 是个字符串,当一些事件发生满足该条件时,一些actions就会被执行

Triggers分为事件Trigger和属性Trigger

事件Trigger由trigger 命令或QueueEventTrigger方法触发.它的格式是个简单的字符串,比如'boot' 或 'late-init'.

属性Trigger是在属性被设置或发生改变时触发. 格式是'property:='或'property:=*',它会在init初始化设置属性的时候触发.

属性Trigger定义的Action可能有多种触发方式,但是事件Trigger定义的Action可能只有一种触发方式

比如:
on boot && property:a=b 定义了action的触发条件是,boot Trigger触发,并且属性a的值等于b

on property:a=b && property:c=d 这个定义有三种触发方式:

  1. 在初始化时,属性a=b,属性c=d.
  2. 在属性c=d的情况下,属性a被改为b.
  3. A在属性a=b的情况下,属性c被改为d.

Commands

bootchart [start|stop]
启动或终止bootcharting. 这个出现在init.rc文件中,但是只有在/data/bootchart/enabled文件存在的时候才有效,否则不能工作

chmod <octal-mode> <path>
修改文件读写权限

chown <owner> <group> <path>
修改文件所有者或所属用户组

class_start <serviceclass>
启动所有以serviceclass命名的未启动的service(service有一个name,也有个class, 这里的serviceclass就是class,class_start和后面的start是两种启动方式,class_start是class形式启动,start是name形式启动)

class_stop <serviceclass>
终止所有以serviceclass命名的正在运行的service

class_reset <serviceclass>
终止所有以serviceclass命名的正在运行的service,但是不禁用它们. 它们可以稍后被class_start重启

class_restart <serviceclass>
重启所有以serviceclass命名的service

copy <src> <dst>
复制一个文件,与write相似,比较适合二进制或比较大的文件.
对于src,从链接文件、world-writable或group-writable复制是不允许的.
对于dst,如果目标文件不存在,则默认权限是0600,如果存在就覆盖掉

domainname <name>
设置域名

enable <servicename>
将一个禁用的service设置为可用. 如果这个service在运行,那么就会重启. 一般用在bootloader时设置属性,然后启动一个service,比如 on property:ro.boot.myfancyhardware=1 enable my_fancy_service_for_my_fancy_hardware exec [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ] 新建子进程并运行一个带指定参数的命令. 这个命令指定了seclabel(安全策略),user(所有者),group(用户组). 直到这个命令运行完才可以运行其他命令,seclabel可以设置为 - 表示用默认值,argument表示属性值. 直到子进程新建完毕,init进程才继续执行.

exec_start <service>
启动一个service,只有当执行结果返回,init进程才能继续执行. 这个跟exec相似,只是将一堆参数的设置改在在service中定义

export <name> <value>
设置环境变量name-value. 这个环境变量将被所有已经启动的service继承

hostname <name>
设置主机名

ifup <interface>
开启指定的网络接口

insmod [-f] <path> [<options>]
安装path下的模块,指定参数options.
-f 表示强制安装,即便是当前Linux内核版本与之不匹配

load_all_props
加载/system, /vendor等目录下的属性,这个用在init.rc中

load_persist_props
加载/data 下的持久化属性. 这个用在init.rc中

loglevel <level>
设置日志输出等级,level表示等级

mkdir <path> [mode] [owner] [group]
创建一个目录,path是路径,mode是读写权限,默认值是755,owner是所有者,默认值root,group是用户组,默认值是root. 如果该目录已存在,则覆盖他们的mode,owner等设置

mount_all <fstab> [ <path> ]\* [--<option>]
当手动触发 "early" 和 "late"时,调用fs_mgr_mount_all 函数,指定fstab配置文件,并导入指定目录下的.rc文件 详情可以查看init.rc文件中的有关定义

mount <type> <device> <dir> [ <flag>\* ] [<options>]
在dir目录下挂载一个名叫device的设备
_flag 包括 "ro", "rw", "remount", "noatime", ...
options 包括 "barrier=1", "noauto_da_alloc", "discard", ... 用逗号分开,比如 barrier=1,noauto_da_alloc

restart <service>
终止后重启一个service,如果这个service刚被重启就什么都不做,如果没有在运行,就启动

restorecon <path> [ <path>\* ]
恢复指定目录下文件的安全上下文.第二个path是安全策略文件. 指定目录不需要必须存在,因为它只需要在init中正确标记

restorecon_recursive <path> [ <path>\* ]
递归地恢复指定目录下的安全上下文,第二个path是安全策略文件位置

rm <path>
调用 unlink(2)删除指定文件. 最好用exec -- rm ...代替,因为这样可以确保系统分区已经挂载好

rmdir <path>
调用 rmdir(2) 删除指定目录

setprop <name> <value>
设置属性name-value

setrlimit <resource> <cur> <max>
指定一个进程的资源限制

start <service>
启动一个未运行的service

stop <service>
终止一个正在运行的service

swapon_all <fstab>
调用 fs_mgr_swapon_all,指定fstab配置文件.

symlink <target> <path>
在path下创建一个指向target的链接

sysclktz <mins_west_of_gmt>
重置系统基准时间(如果是格林尼治标准时间则设置为0)

trigger <event>
触发事件event,由一个action触发到另一个action队列

umount <path>
卸载指定path的文件系统

verity_load_state
内部实现是加载dm-verity的状态

verity_update_state <mount-point>
内部实现是设置dm-verity的状态,并且设置partition.mount-point.verified的属性. 用于adb重新挂载, 因为fs_mgr 不能直接设置它。

wait <path> [ <timeout> ]
查看指定路径是否存在. 如果发现则返回,可以设置超时时间,默认值是5秒

wait_for_prop <name> <value>
等待name属性的值被设置为value,如果name的值一旦被设置为value,马上继续

write <path> <content>
打开path下的文件,并用write(2)写入content内容. 如果文件不存在就会被创建,如果存在就会被覆盖掉

Imports

import关键字不是一个命令,但是如果有.rc文件包含它就会马上解析它里面的section,用法如下:

import <path>
解析path下的.rc文件 ,括展当前文件的配置。如果path是个目录,这个目录下所有.rc文件都被解析,但是不会递归, import被用于以下两个地方:
1.在初始化时解析init.rc文件
2.在mount_all时解析{system,vendor,odm}/etc/init/等目录下的.rc文件

后面的内容主要是一些跟调试init进程相关的东西,比如init.svc.可以查看service启动的状态, ro.boottime.init记录一些关键的时间点,Bootcharting是一个图表化的性能监测工具等,由于与语法关系不大,就不作翻译了

明白了.rc文件的语法,我们再来看看init进程是如何解析.rc文件,将这些语法转化为实际执行的代码的

二、 解析.rc文件

之前我们在文档中看到.rc文件主要有根目录下的 /init.rc ,以及{system,vendor,odm}/etc/init/这三个目录下的 *.rc , 然后就是如果有一个特殊目录被设置的话,就替代这些目录,明白这些,下面的代码就好理解了.

  1. int main(int argc, char** argv) {
  2. ...
  3. const BuiltinFunctionMap function_map;
  4. /*
  5. * 1.C++中::表示静态方法调用,相当于java中static的方法
  6. */
  7. Action::set_function_map(&function_map); //将function_map存放到Action中作为成员属性
  8. Parser& parser = Parser::GetInstance();//单例模式,得到Parser对象
  9. /*
  10. * 1.C++中std::make_unique相当于new,它会返回一个std::unique_ptr,即智能指针,可以自动管理内存
  11. * 2.unique_ptr持有对对象的独有权,两个unique_ptr不能指向一个对象,不能进行复制操作只能进行移动操作
  12. * 3.移动操作的函数是 p1=std::move(p) ,这样指针p指向的对象就移动到p1上了
  13. * 4.接下来的这三句代码都是new一个Parser(解析器),然后将它们放到一个map里存起来
  14. * 5.ServiceParser、ActionParser、ImportParser分别对应service action import的解析
  15. */
  16. parser.AddSectionParser("service",std::make_unique<ServiceParser>());
  17. parser.AddSectionParser("on", std::make_unique<ActionParser>());
  18. parser.AddSectionParser("import", std::make_unique<ImportParser>());
  19. std::string bootscript = GetProperty("ro.boot.init_rc", "");
  20. if (bootscript.empty()) {//如果ro.boot.init_rc没有对应的值,则解析/init.rc以及/system/etc/init、/vendor/etc/init、/odm/etc/init这三个目录下的.rc文件
  21. parser.ParseConfig("/init.rc");
  22. parser.set_is_system_etc_init_loaded(
  23. parser.ParseConfig("/system/etc/init"));
  24. parser.set_is_vendor_etc_init_loaded(
  25. parser.ParseConfig("/vendor/etc/init"));
  26. parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
  27. } else {//如果ro.boot.init_rc属性有值就解析属性值
  28. parser.ParseConfig(bootscript);
  29. parser.set_is_system_etc_init_loaded(true);
  30. parser.set_is_vendor_etc_init_loaded(true);
  31. parser.set_is_odm_etc_init_loaded(true);
  32. }
  33. 复制代码

2.1 ParseConfig

定义在 platform/system/core/init/init_parser.cpp

首先是判断传入的是目录还是文件,其实他们都是调用ParseConfigFile,ParseConfigDir就是遍历下该目录中的文件,对文件排个序,然后调用ParseConfigFile.

  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. 复制代码

而ParseConfigFile就是读取文件中的数据后,将数据传递给ParseData函数,最后遍历section_parsers_调用其EndFile函数, EndFile后面再分析,因为是多态实现,我们先看看ParseData

  1. bool Parser::ParseConfigFile(const std::string& path) {
  2. LOG(INFO) << "Parsing file " << path << "...";
  3. Timer t;
  4. std::string data;
  5. if (!read_file(path, &data)) { //将数据读取到data
  6. return false;
  7. }
  8. data.push_back('\n'); // TODO: fix parse_config.
  9. ParseData(path, data); //解析数据
  10. for (const auto& sp : section_parsers_) {
  11. sp.second->EndFile(path);
  12. }
  13. LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
  14. return true;
  15. }
  16. 复制代码

2.2 ParseData

ParseData 定义在 platform/system/core/init/init_parser.cpp

ParseData通过调用next_token函数遍历每一个字符,以空格或""为分割将一行拆分成若干个单词,调用T_TEXT将单词放到args数组中, 当读到回车符就调用T_NEWLINE,在section_parsers_这个map中找到对应的on service import的解析器,执行ParseSection,如果在 map中找不到对应的key,就执行ParseLineSection,当读到0的时候,表示一个Section读取结束,调用T_EOF执行EndSection.

  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()); //将data的内容复制到data_copy中
  4. data_copy.push_back('\0'); //追加一个结束符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;
  12. for (;;) {
  13. switch (next_token(&state)) { // 遍历data_copy中每一个字符
  14. case T_EOF: //如果是文件结尾,则调用EndSection
  15. if (section_parser) {
  16. section_parser->EndSection();
  17. }
  18. return;
  19. case T_NEWLINE://读取了一行数据
  20. state.line++;
  21. if (args.empty()) {
  22. break;
  23. }
  24. /*
  25. * 1.section_parsers_是一个std:map
  26. * 2.C++中std:map的count函数是查找key,相当于Java中Map的contains
  27. * 3.section_parsers_中只有三个key,on service import,之前AddSectionParser函数加入
  28. */
  29. if (section_parsers_.count(args[0])) { //判断是否包含 on service import
  30. if (section_parser) {
  31. section_parser->EndSection();
  32. }
  33. section_parser = section_parsers_[args[0]].get();//取出对应的parser
  34. std::string ret_err;
  35. if (!section_parser->ParseSection(args, &ret_err)) {//解析对应的Section
  36. parse_error(&state, "%s\n", ret_err.c_str());
  37. section_parser = nullptr;
  38. }
  39. } else if (section_parser) { //不包含 on service import则是command或option
  40. std::string ret_err;
  41. if (!section_parser->ParseLineSection(args, state.filename,
  42. state.line, &ret_err)) {//解析command或option
  43. parse_error(&state, "%s\n", ret_err.c_str());
  44. }
  45. }
  46. args.clear();
  47. break;
  48. case T_TEXT: //将读取的一行数据放到args中,args以空格或""作为分割,将一行数据拆分成单词放进数组中
  49. args.emplace_back(state.text);
  50. break;
  51. }
  52. }
  53. }
  54. 复制代码

这里其实涉及到on service import对应的三个解析器ActionParser,ServiceParser,ImportParser,它们是在之前加入到section_parsers_这个map中的

  1. Parser& parser = Parser::GetInstance();
  2. parser.AddSectionParser("service",std::make_unique<ServiceParser>());
  3. parser.AddSectionParser("on", std::make_unique<ActionParser>());
  4. parser.AddSectionParser("import", std::make_unique<ImportParser>());
  5. void Parser::AddSectionParser(const std::string& name,
  6. std::unique_ptr<SectionParser> parser) {
  7. section_parsers_[name] = std::move(parser);
  8. }
  9. 复制代码

它们都是SectionParser的子类,SectionParser有四个纯虚函数,分别是ParseSection、ParseLineSection、EndSection,EndFile.

  1. class SectionParser {
  2. public:
  3. virtual ~SectionParser() {
  4. }
  5. /*
  6. * 1.C++中纯虚函数的定义格式是 virtual作为修饰符,然后赋值给0,相当于Java中的抽象方法
  7. * 2.如果不赋值给0,却以virtual作为修饰符,这种是虚函数,虚函数可以有方法体,相当于Java中父类的方法,主要用于子类的重载
  8. * 3.只要包含纯虚函数的类就是抽象类,不能new,只能通过子类实现,这个跟Java一样
  9. */
  10. virtual bool ParseSection(const std::vector<std::string>& args,
  11. std::string* err) = 0;
  12. virtual bool ParseLineSection(const std::vector<std::string>& args,
  13. const std::string& filename, int line,
  14. std::string* err) const = 0;
  15. virtual void EndSection() = 0;
  16. virtual void EndFile(const std::string& filename) = 0;
  17. };
  18. 复制代码

接下来我将分析这三个Perser的ParseSection、ParseLineSection、EndSection,EndFile具体实现

2.3 ActionParser

定义在platform/system/core/init/action.cpp

我们先看ParseSection,它先将args中下标1到结尾的数据复制到triggers数组中,然后是构建Action对象,调用InitTriggers,解析这些trigger

  1. bool ActionParser::ParseSection(const std::vector<std::string>& args,
  2. std::string* err) {
  3. std::vector<std::string> triggers(args.begin() + 1, args.end()); //将args复制到triggers中,除去下标0
  4. if (triggers.size() < 1) {
  5. *err = "actions must have a trigger";
  6. return false;
  7. }
  8. auto action = std::make_unique<Action>(false);
  9. if (!action->InitTriggers(triggers, err)) { //调用InitTriggers解析trigger
  10. return false;
  11. }
  12. action_ = std::move(action);
  13. return true;
  14. }
  15. 复制代码

InitTriggers通过比较是否以"property:"开头,区分trigger的类型,如果是property trigger,就调用ParsePropertyTrigger, 如果是event trigger,就将args的参数赋值给event_trigger_,类型是string

  1. bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
  2. const static std::string prop_str("property:");
  3. for (std::size_t i = 0; i < args.size(); ++i) {
  4. ...
  5. if (!args[i].compare(0, prop_str.length(), prop_str)) {
  6. if (!ParsePropertyTrigger(args[i], err)) {
  7. return false;
  8. }
  9. } else {
  10. ...
  11. event_trigger_ = args[i];
  12. }
  13. }
  14. return true;
  15. }
  16. 复制代码

ParsePropertyTrigger函数先是将字符以"="分割为name-value,然后将name-value存入property_triggers_这个map中

  1. bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
  2. const static std::string prop_str("property:");
  3. std::string prop_name(trigger.substr(prop_str.length())); //截取property:后的内容
  4. size_t equal_pos = prop_name.find('=');
  5. if (equal_pos == std::string::npos) {
  6. *err = "property trigger found without matching '='";
  7. return false;
  8. }
  9. std::string prop_value(prop_name.substr(equal_pos + 1)); //取出value
  10. prop_name.erase(equal_pos); //删除下标为equal_pos的字符,也就是删除"="
  11. if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
  12. //将name-value存放到map中,emplace相当于put操作
  13. *err = "multiple property triggers found for same property";
  14. return false;
  15. }
  16. return true;
  17. }
  18. 复制代码

从上面看出,ParseSection函数的作用就是构造一个Action对象,将trigger条件记录到Action这个对象中,如果是event trigger就赋值给event_trigger_, 如果是property trigger就存放到property_triggers_这个map中. 接下来我们分析ParseLineSection

ParseLineSection是直接调用Action对象的AddCommand函数

  1. bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
  2. const std::string& filename, int line,
  3. std::string* err) const {
  4. return action_ ? action_->AddCommand(args, filename, line, err) : false;
  5. }
  6. 复制代码

AddCommand看名字就大概知道是添加命令,它首先是做一些参数空值的检查,然后是调用FindFunction查找命令对应的执行函数, 最后将这些信息包装成Command对象存放到commands_数组中,这里比较关键的就是FindFunction

  1. bool Action::AddCommand(const std::vector<std::string>& args,
  2. const std::string& filename, int line, std::string* err) {
  3. ... //一些参数检查
  4. auto function = function_map_->FindFunction(args[0], args.size() - 1, err);//查找命令对应的执行函数
  5. if (!function) {
  6. return false;
  7. }
  8. AddCommand(function, args, filename, line);
  9. return true;
  10. }
  11. void Action::AddCommand(BuiltinFunction f,
  12. const std::vector<std::string>& args,
  13. const std::string& filename, int line) {
  14. commands_.emplace_back(f, args, filename, line);//commands_是个数组,emplace_back就相当于add
  15. }
  16. 复制代码

FindFunction定义在platform/system/core/init/keyword_map.h

这个函数主要作用是通过命令查找对应的执行函数,比如.rc文件中定义chmod,那我们得找到chmod具体去执行哪个函数. 它首先是通过map()返回一个std:map,调用其find函数, find相当于Java中的get,但是返回的是entry,可以通过entry ->first和entry ->second获取key-value. 找到的value是一个结构体,里面有三个值,第一个是参数最小数目,第二个是参数最大数目,第三个就是执行函数, 之后作了参数的数目检查,也就是说命令后的参数要在最小值和最大值之间.

  1. const Function FindFunction(const std::string& keyword,
  2. size_t num_args,
  3. std::string* err) const {
  4. using android::base::StringPrintf;
  5. auto function_info_it = map().find(keyword); //找到keyword对应的entry
  6. if (function_info_it == map().end()) { // end是最后一个元素后的元素,表示找不到
  7. *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
  8. return nullptr;
  9. }
  10. auto function_info = function_info_it->second;//获取value
  11. auto min_args = std::get<0>(function_info);//获取参数数量最小值
  12. auto max_args = std::get<1>(function_info);//获取参数数量最大值
  13. if (min_args == max_args && num_args != min_args) {//将实际参数数量与最大值最小值比较
  14. *err = StringPrintf("%s requires %zu argument%s",
  15. keyword.c_str(), min_args,
  16. (min_args > 1 || min_args == 0) ? "s" : "");
  17. return nullptr;
  18. }
  19. if (num_args < min_args || num_args > max_args) {
  20. if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
  21. *err = StringPrintf("%s requires at least %zu argument%s",
  22. keyword.c_str(), min_args,
  23. min_args > 1 ? "s" : "");
  24. } else {
  25. *err = StringPrintf("%s requires between %zu and %zu arguments",
  26. keyword.c_str(), min_args, max_args);
  27. }
  28. return nullptr;
  29. }
  30. return std::get<Function>(function_info);//返回命令对应的执行函数
  31. }
  32. 复制代码

我们看看map()的实现,定义在platform/system/core/init/builtins.cpp

这个实现比较简单,就是直接构造一个map,然后返回. 比如{"bootchart", {1,1,do_bootchart}}, 表示命令名称叫bootchart,对应的执行函数是do_bootchart,允许传入的最小和最大参数数量是1

  1. BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
  2. constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); //表示size_t的最大值
  3. // clang-format off
  4. static const Map builtin_functions = {
  5. {"bootchart", {1, 1, do_bootchart}},
  6. {"chmod", {2, 2, do_chmod}},
  7. {"chown", {2, 3, do_chown}},
  8. {"class_reset", {1, 1, do_class_reset}},
  9. {"class_restart", {1, 1, do_class_restart}},
  10. {"class_start", {1, 1, do_class_start}},
  11. {"class_stop", {1, 1, do_class_stop}},
  12. {"copy", {2, 2, do_copy}},
  13. {"domainname", {1, 1, do_domainname}},
  14. {"enable", {1, 1, do_enable}},
  15. {"exec", {1, kMax, do_exec}},
  16. {"exec_start", {1, 1, do_exec_start}},
  17. {"export", {2, 2, do_export}},
  18. {"hostname", {1, 1, do_hostname}},
  19. {"ifup", {1, 1, do_ifup}},
  20. {"init_user0", {0, 0, do_init_user0}},
  21. {"insmod", {1, kMax, do_insmod}},
  22. {"installkey", {1, 1, do_installkey}},
  23. {"load_persist_props", {0, 0, do_load_persist_props}},
  24. {"load_system_props", {0, 0, do_load_system_props}},
  25. {"loglevel", {1, 1, do_loglevel}},
  26. {"mkdir", {1, 4, do_mkdir}},
  27. {"mount_all", {1, kMax, do_mount_all}},
  28. {"mount", {3, kMax, do_mount}},
  29. {"umount", {1, 1, do_umount}},
  30. {"restart", {1, 1, do_restart}},
  31. {"restorecon", {1, kMax, do_restorecon}},
  32. {"restorecon_recursive", {1, kMax, do_restorecon_recursive}},
  33. {"rm", {1, 1, do_rm}},
  34. {"rmdir", {1, 1, do_rmdir}},
  35. {"setprop", {2, 2, do_setprop}},
  36. {"setrlimit", {3, 3, do_setrlimit}},
  37. {"start", {1, 1, do_start}},
  38. {"stop", {1, 1, do_stop}},
  39. {"swapon_all", {1, 1, do_swapon_all}},
  40. {"symlink", {2, 2, do_symlink}},
  41. {"sysclktz", {1, 1, do_sysclktz}},
  42. {"trigger", {1, 1, do_trigger}},
  43. {"verity_load_state", {0, 0, do_verity_load_state}},
  44. {"verity_update_state", {0, 0, do_verity_update_state}},
  45. {"wait", {1, 2, do_wait}},
  46. {"wait_for_prop", {2, 2, do_wait_for_prop}},
  47. {"write", {2, 2, do_write}},
  48. };
  49. // clang-format on
  50. return builtin_functions;
  51. }
  52. 复制代码

接下来我们看看EndSection,直接是调用ActionManager::GetInstance().AddAction

  1. void ActionParser::EndSection() {
  2. if (action_ && action_->NumCommands() > 0) {
  3. ActionManager::GetInstance().AddAction(std::move(action_));
  4. }
  5. }
  6. 复制代码

AddAction首先是查找是否有存在的同名Action,如果有就将他们的命令合并,没有就将它存入数组actions_中

  1. void ActionManager::AddAction(std::unique_ptr<Action> action) {
  2. auto old_action_it =
  3. std::find_if(actions_.begin(), actions_.end(),
  4. [&action] (std::unique_ptr<Action>& a) {
  5. return action->TriggersEqual(*a);
  6. });//find_if是集合中用于比较的模板,上面这种写法是lambda表达式
  7. if (old_action_it != actions_.end()) {//在数组actions中找到Action说明已经存在同名,就合并command
  8. (*old_action_it)->CombineAction(*action);
  9. } else { //找不到就加入数组
  10. actions_.emplace_back(std::move(action));
  11. }
  12. }
  13. bool Action::TriggersEqual(const Action& other) const {
  14. return property_triggers_ == other.property_triggers_ &&
  15. event_trigger_ == other.event_trigger_;//比较之前记录的event trigger和property trigger
  16. }
  17. void Action::CombineAction(const Action& action) {
  18. for (const auto& c : action.commands_) { //将新的Action中的command合并到老的Action
  19. commands_.emplace_back(c);
  20. }
  21. }
  22. 复制代码

EndFile是一个空实现,定义在platform/system/core/init/action.h

  1. class ActionParser : public SectionParser {
  2. public:
  3. ActionParser() : action_(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. std::unique_ptr<Action> action_;
  15. };
  16. 复制代码

讲了这么多,小结一下ActionParser做的事情. 它有三个重要的重载函数,ParseSection、ParseLineSection、EndSection.

  • ParseSection函数的作用是构造一个Action对象,将trigger条件记录到Action这个对象中
  • ParseLineSection作用是根据命令在一个map中找到对应的执行函数,然后将信息记录到之前构造的Action中
  • EndSection作用是将前两步构造的Action存入一个数组中,存入之前比较下数组中是否已经存在同名的Action,如果有就合并command

2.4 ServiceParser

定义在platform/system/core/init/service.cpp

我们还是分析它的四个函数ParseSection、ParseLineSection、EndSection、EndFile

ParseSection首先是判断单词个数至少有三个,因为必须有一个服务名称和执行文件,然后是判断名称是否合法,主要是一些长度及内容的检查,最后就是构造一个Service对象

  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());
  13. service_ = std::make_unique<Service>(name, str_args);// 构造Service对象
  14. return true;
  15. }
  16. 复制代码

ParseLineSection直接执行Service的ParseLine函数

  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. 复制代码

ParseLine的思路跟之前Action一样,就是根据option名称从map中找到对应的执行函数,然后执行这个函数. 这些执行函数主要作用就是对传入参数做一些处理,然后将信息记录到Service对象中

  1. bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
  2. if (args.empty()) {
  3. *err = "option needed, but not provided";
  4. return false;
  5. }
  6. static const OptionParserMap parser_map;
  7. auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);//从map中找出执行函数
  8. if (!parser) {
  9. return false;
  10. }
  11. return (this->*parser)(args, err);//执行找到的这个函数
  12. }
  13. 复制代码

map()返回的map如下,定义在定义在platform/system/core/init/service.cpp中

  1. Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
  2. constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
  3. // clang-format off
  4. static const Map option_parsers = {
  5. {"capabilities",
  6. {1, kMax, &Service::ParseCapabilities}},
  7. {"class", {1, kMax, &Service::ParseClass}},
  8. {"console", {0, 1, &Service::ParseConsole}},
  9. {"critical", {0, 0, &Service::ParseCritical}},
  10. {"disabled", {0, 0, &Service::ParseDisabled}},
  11. {"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
  12. {"ioprio", {2, 2, &Service::ParseIoprio}},
  13. {"priority", {1, 1, &Service::ParsePriority}},
  14. {"keycodes", {1, kMax, &Service::ParseKeycodes}},
  15. {"oneshot", {0, 0, &Service::ParseOneshot}},
  16. {"onrestart", {1, kMax, &Service::ParseOnrestart}},
  17. {"oom_score_adjust",
  18. {1, 1, &Service::ParseOomScoreAdjust}},
  19. {"namespace", {1, 2, &Service::ParseNamespace}},
  20. {"seclabel", {1, 1, &Service::ParseSeclabel}},
  21. {"setenv", {2, 2, &Service::ParseSetenv}},
  22. {"socket", {3, 6, &Service::ParseSocket}},
  23. {"file", {2, 2, &Service::ParseFile}},
  24. {"user", {1, 1, &Service::ParseUser}},
  25. {"writepid", {1, kMax, &Service::ParseWritepid}},
  26. };
  27. // clang-format on
  28. return option_parsers;
  29. }
  30. 复制代码

接下来我们看看EndSection,直接调用ServiceManager的AddService函数

  1. void ServiceParser::EndSection() {
  2. if (service_) {
  3. ServiceManager::GetInstance().AddService(std::move(service_));
  4. }
  5. }
  6. 复制代码

AddService的实现比较简单,就是通过比较service的name,查看存放Service的数组services_中是否有同名的service,如果有就打印下错误日志,直接返回, 如果不存在就加入数组中

  1. void ServiceManager::AddService(std::unique_ptr<Service> service) {
  2. Service* old_service = FindServiceByName(service->name()); //查找services_中是否已存在同名service
  3. if (old_service) {
  4. LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
  5. return;
  6. }
  7. services_.emplace_back(std::move(service));//加入数组
  8. }
  9. Service* ServiceManager::FindServiceByName(const std::string& name) const {
  10. auto svc = std::find_if(services_.begin(), services_.end(),
  11. [&name] (const std::unique_ptr<Service>& s) {
  12. return name == s->name();
  13. });//跟之前action一样,遍历数组进行比较,查找同名service
  14. if (svc != services_.end()) {
  15. return svc->get(); //找到就返回service
  16. }
  17. return nullptr;
  18. }
  19. 复制代码

EndFile依然是一个空实现,定义在platform/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. };
  17. 复制代码

从上面可以看出,ServiceParser的处理跟ActionParser差不多,区别在于Action将执行函数存起来等待Trigger触发时执行,Service找到执行函数后是马上执行

2.4 ImportParser

定义在platform/system/core/init/import_parser.cpp

最后我们看看ImportParser,ImportParser的ParseLineSection、EndSection都是空实现,只实现了ParseSection和EndFile, 因为它的语法比较单一,只有一行. 我们来看看它的ParseSection函数

首先检查单词只能是两个,因为只能是import xxx 这种语法,然后调用expand_props处理下参数,最后将结果放入数组imports_存起来

  1. bool ImportParser::ParseSection(const std::vector<std::string>& args,
  2. std::string* err) {
  3. if (args.size() != 2) { //检查参数只能是两个
  4. *err = "single argument needed for import\n";
  5. return false;
  6. }
  7. std::string conf_file;
  8. bool ret = expand_props(args[1], &conf_file); //处理第二个参数
  9. if (!ret) {
  10. *err = "error while expanding import";
  11. return false;
  12. }
  13. LOG(INFO) << "Added '" << conf_file << "' to import list";
  14. imports_.emplace_back(std::move(conf_file)); //存入数组
  15. return true;
  16. }
  17. 复制代码

expand_props 定义在platform/system/core/init/util.cpp ,主要作用就是找到{x.y}或x.y这种语法,将x.y取出来作为name,去属性系统中找对应的value,然后替换

  1. bool expand_props(const std::string& src, std::string* dst) {
  2. const char* src_ptr = src.c_str();
  3. if (!dst) {
  4. return false;
  5. }
  6. /* - variables can either be $x.y or ${x.y}, in case they are only part
  7. * of the string.
  8. * - will accept $$ as a literal $.
  9. * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
  10. * bad things will happen
  11. * - ${x.y:-default} will return default value if property empty.
  12. */
  13. //这段英文大概的意思是 参数要么是$x.y,要么是${x.y},它们都是路径的一部分,$$表示字符 $ ,
  14. //${foo.${bar}}这种递归写法是不支持的,因为会发生一些糟糕的事情
  15. //${x.y:-default}会将default作为默认值返回,如果找不到对应的属性值的话
  16. while (*src_ptr) {
  17. const char* c;
  18. c = strchr(src_ptr, '$');
  19. if (!c) { // 找不到$符号,直接将dst赋值为src返回
  20. dst->append(src_ptr);
  21. return true;
  22. }
  23. dst->append(src_ptr, c);
  24. c++;
  25. if (*c == '$') { //跳过$
  26. dst->push_back(*(c++));
  27. src_ptr = c;
  28. continue;
  29. } else if (*c == '\0') {
  30. return true;
  31. }
  32. std::string prop_name;
  33. std::string def_val;
  34. if (*c == '{') { //找到 { 就准备找 }的下标,然后截取它们之间的字符串,对应${x.y}的情况
  35. c++;
  36. const char* end = strchr(c, '}');
  37. if (!end) {
  38. // failed to find closing brace, abort.
  39. LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }";
  40. return false;
  41. }
  42. prop_name = std::string(c, end); //截取{}之间的字符串作为name
  43. c = end + 1;
  44. size_t def = prop_name.find(":-"); //如果发现有 ":-" ,就将后面的值作为默认值先存起来
  45. if (def < prop_name.size()) {
  46. def_val = prop_name.substr(def + 2);
  47. prop_name = prop_name.substr(0, def);
  48. }
  49. } else { //对应$x.y的情况
  50. prop_name = c;
  51. LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead";
  52. c += prop_name.size();
  53. }
  54. if (prop_name.empty()) {
  55. LOG(ERROR) << "invalid zero-length property name in '" << src << "'";
  56. return false;
  57. }
  58. std::string prop_val = android::base::GetProperty(prop_name, ""); //通过name在属性系统中找对应的value,内部调用的是之前属性系统的__system_property_find函数
  59. if (prop_val.empty()) { //没有找到值就返回默认值
  60. if (def_val.empty()) {
  61. LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
  62. return false;
  63. }
  64. prop_val = def_val;
  65. }
  66. dst->append(prop_val);
  67. src_ptr = c;
  68. }
  69. return true;
  70. }
  71. 复制代码

EndFile的实现比较简单,就是复制下ParseSection函数解析的.rc文件数组,然后遍历数组,调用最开始的ParseConfig函数解析一个完整的路径

  1. void ImportParser::EndFile(const std::string& filename) {
  2. auto current_imports = std::move(imports_);
  3. imports_.clear();
  4. for (const auto& s : current_imports) {
  5. if (!Parser::GetInstance().ParseConfig(s)) {
  6. PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
  7. }
  8. }
  9. }
  10. 复制代码

由此,我们将Android Init Language语法的转化过程分析完毕,其实它们核心的解析器就三个,ActionParser,ServiceParser,ImportParser. 而这几个解析器主要是实现ParseSection、ParseLineSection、EndSection、EndFile四个函数

  • ParseSection用于解析Section的第一行,比如
  1. on early
  2. service ueventd /sbin/ueventd
  3. import /init.${ro.zygote}.rc
  4. 复制代码
  • ParseLineSection用于解析Section的command或option,比如
  1. write /proc/1/oom_score_adj -1000
  2. class core
  3. 复制代码
  • EndSection用于处理Action和Service同名的情况,以及将解析的对象存入数组备用
  • EndFile只有在ImportParser中有用到,主要是解析导入的.rc文件

三、加入一些事件和一些Action

经过上一步的解析,系统从各种.rc文件中读取了需要执行的Action和Service,但是还是需要一些额外的配置,也需要加入触发条件准备去触发

  1. // Turning this on and letting the INFO logging be discarded adds 0.2s to
  2. // Nexus 9 boot time, so it's disabled by default.
  3. if (false) parser.DumpState(); //打印一些当前Parser的信息,默认是不执行的
  4. ActionManager& am = ActionManager::GetInstance();
  5. am.QueueEventTrigger("early-init");//QueueEventTrigger用于触发Action,这里触发 early-init事件
  6. // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
  7. am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
  8. //QueueBuiltinAction用于添加Action,第一个参数是Action要执行的Command,第二个是Trigger
  9. // ... so that we can start queuing up actions that require stuff from /dev.
  10. am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
  11. am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
  12. am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
  13. am.QueueBuiltinAction(keychord_init_action, "keychord_init");
  14. am.QueueBuiltinAction(console_init_action, "console_init");
  15. // Trigger all the boot actions to get us started.
  16. am.QueueEventTrigger("init");
  17. // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
  18. // wasn't ready immediately after wait_for_coldboot_done
  19. am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
  20. // Don't mount filesystems or start core system services in charger mode.
  21. std::string bootmode = GetProperty("ro.bootmode", "");
  22. if (bootmode == "charger") {
  23. am.QueueEventTrigger("charger");
  24. } else {
  25. am.QueueEventTrigger("late-init");
  26. }
  27. // Run all property triggers based on current state of the properties.
  28. am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
  29. 复制代码

3.1 QueueEventTrigger

定义在platform/system/core/init/action.cpp

它并没有去触发trigger,而是构造了一个EventTrigger对象,放到队列中存起来

  1. void ActionManager::QueueEventTrigger(const std::string& trigger) {
  2. trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
  3. }
  4. class EventTrigger : public Trigger {
  5. public:
  6. explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
  7. }
  8. bool CheckTriggers(const Action& action) const override {
  9. return action.CheckEventTrigger(trigger_);
  10. }
  11. private:
  12. const std::string trigger_;
  13. };
  14. 复制代码

3.2 QueueBuiltinAction

定义在platform/system/core/init/action.cpp

这个函数有两个参数,第一个参数是一个函数指针,第二参数是字符串. 首先是创建一个Action对象,将第二参数作为Action触发条件, 将第一个参数作为Action触发后的执行命令,并且又把第二个参数作为命令的参数,最后是将Action加入触发队列并加入Action列表

  1. void ActionManager::QueueBuiltinAction(BuiltinFunction func,
  2. const std::string& name) {
  3. auto action = std::make_unique<Action>(true);
  4. std::vector<std::string> name_vector{name};
  5. if (!action->InitSingleTrigger(name)) { //调用InitTriggers,之前讲过用于将name加入Action的trigger列表
  6. return;
  7. }
  8. action->AddCommand(func, name_vector);//加入Action的command列表
  9. trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));//将Action加入触发队列
  10. actions_.emplace_back(std::move(action));//加入Action列表
  11. }
  12. 复制代码

四、触发所有事件并不断监听新的事件

之前的所有工作都是往各种数组、队列里面存入信息,并没有真正去触发,而接下来的工作就是真正去触发这些事件,以及用epoll不断监听新的事件

  1. while (true) {
  2. // By default, sleep until something happens.
  3. int epoll_timeout_ms = -1; //epoll超时时间,相当于阻塞时间
  4. /*
  5. * 1.waiting_for_prop和IsWaitingForExec都是判断一个Timer为不为空,相当于一个标志位
  6. * 2.waiting_for_prop负责属性设置,IsWaitingForExe负责service运行
  7. * 3.当有属性设置或Service开始运行时,这两个值就不为空,直到执行完毕才置为空
  8. * 4.其实这两个判断条件主要作用就是保证属性设置和service启动的完整性,也可以说是为了同步
  9. */
  10. if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
  11. am.ExecuteOneCommand(); //执行一个command
  12. }
  13. if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
  14. restart_processes(); //重启服务
  15. // If there's a process that needs restarting, wake up in time for that.
  16. if (process_needs_restart_at != 0) { //当有进程需要重启时,设置epoll_timeout_ms为重启等待时间
  17. epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
  18. if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
  19. }
  20. // If there's more work to do, wake up again immediately.
  21. if (am.HasMoreCommands()) epoll_timeout_ms = 0; //当还有命令要执行时,将epoll_timeout_ms设置为0
  22. }
  23. epoll_event ev;
  24. /*
  25. * 1.epoll_wait与上一篇中讲的epoll_create1、epoll_ctl是一起使用的
  26. * 2.epoll_create1用于创建epoll的文件描述符,epoll_ctl、epoll_wait都把它创建的fd作为第一个参数传入
  27. * 3.epoll_ctl用于操作epoll,EPOLL_CTL_ADD:注册新的fd到epfd中,EPOLL_CTL_MOD:修改已经注册的fd的监听事件,EPOLL_CTL_DEL:从epfd中删除一个fd;
  28. * 4.epoll_wait用于等待事件的产生,epoll_ctl调用EPOLL_CTL_ADD时会传入需要监听什么类型的事件,
  29. * 比如EPOLLIN表示监听fd可读,当该fd有可读的数据时,调用epoll_wait经过epoll_timeout_ms时间就会把该事件的信息返回给&ev
  30. */
  31. int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
  32. if (nr == -1) {
  33. PLOG(ERROR) << "epoll_wait failed";
  34. } else if (nr == 1) {
  35. ((void (*)()) ev.data.ptr)();//当有event返回时,取出ev.data.ptr(之前epoll_ctl注册时的回调函数),直接执行
  36. //上一篇中在signal_handler_init和start_property_service有注册两个fd的监听,一个用于监听SIGCHLD(子进程结束信号),一个用于监听属性设置
  37. }
  38. }
  39. return 0;
  40. }
  41. 复制代码

4.1 ExecuteOneCommand

定义在platform/system/core/init/action.cpp

从名字可以看出,它只执行一个command,是的,只执行一个. 在函数一开始就从trigger_queue_队列中取出一个trigger, 然后遍历所有action,找出满足trigger条件的action加入待执行列表current_executing_actions_中, 接着从这个列表中取出一个action,执行它的第一个命令,并将命令所在下标自加1. 由于ExecuteOneCommand外部是一个无限循环, 因此按照上面的逻辑一遍遍执行,将按照trigger表的顺序,依次执行满足trigger条件的action,然后依次执行action中的命令.

  1. void ActionManager::ExecuteOneCommand() {
  2. // Loop through the trigger queue until we have an action to execute
  3. while (current_executing_actions_.empty() && !trigger_queue_.empty()) {//current_executing_actions_.empty保证了一次只遍历一个trigger
  4. for (const auto& action : actions_) {//遍历所有的Action
  5. if (trigger_queue_.front()->CheckTriggers(*action)) {//满足当前Trigger条件的就加入队列current_executing_actions_
  6. current_executing_actions_.emplace(action.get());
  7. }
  8. }
  9. trigger_queue_.pop();//从trigger_queue_中踢除一个trigger
  10. }
  11. if (current_executing_actions_.empty()) {
  12. return;
  13. }
  14. auto action = current_executing_actions_.front();//从满足trigger条件的action队列中取出一个action
  15. if (current_command_ == 0) {
  16. std::string trigger_name = action->BuildTriggersString();
  17. LOG(INFO) << "processing action (" << trigger_name << ")";
  18. }
  19. action->ExecuteOneCommand(current_command_);//执行该action中的第current_command_个命令
  20. // If this was the last command in the current action, then remove
  21. // the action from the executing list.
  22. // If this action was oneshot, then also remove it from actions_.
  23. ++current_command_; //下标加1
  24. if (current_command_ == action->NumCommands()) { //如果是最后一条命令
  25. current_executing_actions_.pop();//将该action从current_executing_actions_中踢除
  26. current_command_ = 0;
  27. if (action->oneshot()) {//如果action只执行一次,将该action从数组actions_中踢除
  28. auto eraser = [&action] (std::unique_ptr<Action>& a) {
  29. return a.get() == action;
  30. };
  31. actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
  32. }
  33. }
  34. }
  35. 复制代码

4.1 restart_processes

定义在platform/system/core/init/init.cpp

restart_processes调用的其实是ForEachServiceWithFlags函数,这个函数主要是遍历services_数组,比较它们的flags是否是SVC_RESTARTING, 也就是当前service是否是等待重启的,如果是就执行它的RestartIfNeeded函数

  1. static void restart_processes()
  2. {
  3. process_needs_restart_at = 0;
  4. ServiceManager::GetInstance().ForEachServiceWithFlags(SVC_RESTARTING, [](Service* s) {
  5. s->RestartIfNeeded(&process_needs_restart_at);
  6. });
  7. }
  8. void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
  9. void (*func)(Service* svc)) const {
  10. for (const auto& s : services_) { //遍历所有service
  11. if (s->flags() & matchflags) {//找出flags是SVC_RESTARTING的,执行func,也就是传入的RestartIfNeeded
  12. func(s.get());
  13. }
  14. }
  15. }
  16. 复制代码

4.2 RestartIfNeeded

定义在platform/system/core/init/service.cpp

这个函数将主要工作交给了Start,也就是具体的启动service,但是交给它之前做了一些判断,也就是5秒内只能启动一个服务, 如果有多个服务,那么后续的服务将进入等待

  1. void Service::RestartIfNeeded(time_t* process_needs_restart_at) {
  2. boot_clock::time_point now = boot_clock::now();
  3. boot_clock::time_point next_start = time_started_ + 5s; //time_started_是上一个service启动的时间戳
  4. if (now > next_start) { //也就是说两个服务进程启动的间隔必须大于5s
  5. flags_ &= (~SVC_RESTARTING); // &= 加 ~ 相当于取消标记
  6. Start();
  7. return;
  8. }
  9. time_t next_start_time_t = time(nullptr) +
  10. time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count());
  11. if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) {
  12. *process_needs_restart_at = next_start_time_t;//如果两个service启动间隔小于5s,将剩余时间赋值给process_needs_restart_at
  13. }
  14. }
  15. 复制代码

4.2 Start

定义在platform/system/core/init/service.cpp

Start是具体去启动服务了,它主要是调用clone或fork创建子进程,然后调用execve执行配置的二进制文件,另外根据之前在.rc文件中的配置,去执行这些配置

  1. bool Service::Start() {
  2. ... //清空标记,根据service的配置初始化console、SELinux策略等
  3. LOG(INFO) << "starting service '" << name_ << "'...";
  4. pid_t pid = -1;
  5. if (namespace_flags_) {//这个标记当service定义了namespace时会赋值为CLONE_NEWPID|CLONE_NEWNS
  6. pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr); //以clone方式在新的namespace创建子进程
  7. } else {
  8. pid = fork();//以fork方式创建子进程
  9. }
  10. if (pid == 0) {//表示创建子进程成功
  11. ... //执行service配置的其他参数,比如setenv、writepid等
  12. std::vector<char*> strs;
  13. ExpandArgs(args_, &strs);//将args_解析一下,比如有${x.y},然后赋值表strs
  14. if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) { //执行系统调用execve,也就是执行配置的二进制文件,把参数传进去
  15. PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
  16. }
  17. _exit(127);
  18. }
  19. if (pid < 0) { //子进程创建失败
  20. PLOG(ERROR) << "failed to fork for '" << name_ << "'";
  21. pid_ = 0;
  22. return false;
  23. }
  24. ... //执行service其他参数如oom_score_adjust_,改变service运行状态等
  25. }
  26. 复制代码

小结

这一阶段Init进程做了许多重要的事情,比如解析.rc文件,这里配置了所有需要执行的action和需要启动的service, Init进程根据语法一步步去解析.rc,将这些配置转换成一个个数组、队列,然后开启无限循环去处理这些数组、队列中的command和service,并且通过epoll监听子进程结束和属性设置.

至此,我已经将Init进程的三个阶段讲解完了,下一篇我将讲解.rc中配置的一个重要的service--zygote,它是我们app程序的鼻祖.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/318956
推荐阅读
相关标签
  

闽ICP备14008679号