这是一个连载的博文系列,我将持续为你们提供尽量透彻的Android源码分析 github连载地址html
init通过前两个阶段后,已经创建了属性系统和SELinux系统,可是init进程还须要执行不少其余的操做,还要启动许多关键的系统服务, 可是若是都是像属性系统和SELinux系统那样一行行代码去作,显得有点杂乱繁琐,并且不容易扩展,因此Android系统引入了init.rcjava
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文件的linux
本文主要讲解如下内容android
本文涉及到的文件ios
platform/system/core/init/README.md
platform/system/core/init/init.cpp
platform/system/core/init/init_parser.cpp
platform/system/core/init/action.cpp
platform/system/core/init/action.h
platform/system/core/init/keyword_map.h
platform/system/core/init/builtins.cpp
platform/system/core/init/service.cpp
platform/system/core/init/service.h
platform/system/core/init/import_parser.cpp
platform/system/core/init/util.cpp
复制代码
定义在platform/system/core/init/README.mdc++
.rc文件主要配置了两个东西,一个是action,一个是service,trigger和command是对action的补充,options是对service的补充. action加上trigger以及一些command,组成一个Section,service加上一些option,也组成一个Section ,.rc文件就是由一个个Section组成. .rc文件头部有一个import的语法,表示这些.rc也一并包含并解析,接下来咱们重点讲下action和service.git
action的格式以下:github
on <trigger> [&& <trigger>]* <command> <command> <command> 复制代码
以on开头,trigger是判断条件,command是具体执行一些操做,当知足trigger条件时,执行这些command
trigger能够是一个字符串,如数组
on early //表示当trigger early或QueueEventTrigger("early")调用时触发 复制代码
也能够是属性,如安全
on property:sys.boot_from_charger_mode=1//表示当sys.boot_from_charger_mode的值经过property_set设置为1时触发 on property:sys.sysctl.tcp_def_init_rwnd=* // *表示任意值 复制代码
条件能够是多个,用&&链接,如
on zygote-start && property:ro.crypto.state=unencrypted //表示当zygote-start触发而且ro.crypto.state属性值为unencrypted时触发 复制代码
command就是一些具体的操做,如
mkdir /dev/fscklogs 0770 root system //新建目录 class_stop charger //终止服务 trigger late-init //触发late-init 复制代码
services的格式以下:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
复制代码
以service开头,name是指定这个服务的名称,pathname表示这个服务的执行文件路径,argument表示执行文件带的参数,option表示这个服务的一些配置 咱们看一个典型的例子就知道了
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote class main priority -20 user root group root readproc socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart netd onrestart restart wificond writepid /dev/cpuset/foreground/tasks 复制代码
这个是配置在 /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服务
有一个特殊的目录可能被用来替换上面的三个默认目录,这主要是为了支持工厂模式和其余非标准的启动模式,上面三个目录用于正常的启动过程
这三个用于扩展的目录是
- /system/etc/init/ 用于系统自己,好比SurfaceFlinger, MediaService, and logcatd.
- /vendor/etc/init/ 用于SoC(系统级核心厂商,如高通),为他们提供一些核心功能和服务
- /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的格式以下:
on <trigger> [&& <trigger>]* <command> <command> <command> 复制代码
Services
Services是init进程启动的程序,它们也可能在退出时自动重启. Services的格式以下:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
复制代码
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
这个定义有三种触发方式:
- 在初始化时,属性a=b,属性c=d.
- 在属性c=d的状况下,属性a被改成b.
- 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_hardwareexec [ <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文件主要有根目录下的 /init.rc ,以及{system,vendor,odm}/etc/init/这三个目录下的 *.rc , 而后就是若是有一个特殊目录被设置的话,就替代这些目录,明白这些,下面的代码就好理解了.
int main(int argc, char** argv) { ... const BuiltinFunctionMap function_map; /* * 1.C++中::表示静态方法调用,至关于java中static的方法 */ Action::set_function_map(&function_map); //将function_map存放到Action中做为成员属性 Parser& parser = Parser::GetInstance();//单例模式,获得Parser对象 /* * 1.C++中std::make_unique至关于new,它会返回一个std::unique_ptr,即智能指针,能够自动管理内存 * 2.unique_ptr持有对对象的独有权,两个unique_ptr不能指向一个对象,不能进行复制操做只能进行移动操做 * 3.移动操做的函数是 p1=std::move(p) ,这样指针p指向的对象就移动到p1上了 * 4.接下来的这三句代码都是new一个Parser(解析器),而后将它们放到一个map里存起来 * 5.ServiceParser、ActionParser、ImportParser分别对应service action import的解析 */ parser.AddSectionParser("service",std::make_unique<ServiceParser>()); parser.AddSectionParser("on", std::make_unique<ActionParser>()); parser.AddSectionParser("import", std::make_unique<ImportParser>()); std::string bootscript = GetProperty("ro.boot.init_rc", ""); if (bootscript.empty()) {//若是ro.boot.init_rc没有对应的值,则解析/init.rc以及/system/etc/init、/vendor/etc/init、/odm/etc/init这三个目录下的.rc文件 parser.ParseConfig("/init.rc"); parser.set_is_system_etc_init_loaded( parser.ParseConfig("/system/etc/init")); parser.set_is_vendor_etc_init_loaded( parser.ParseConfig("/vendor/etc/init")); parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init")); } else {//若是ro.boot.init_rc属性有值就解析属性值 parser.ParseConfig(bootscript); parser.set_is_system_etc_init_loaded(true); parser.set_is_vendor_etc_init_loaded(true); parser.set_is_odm_etc_init_loaded(true); } 复制代码
定义在 platform/system/core/init/init_parser.cpp
首先是判断传入的是目录仍是文件,其实他们都是调用ParseConfigFile,ParseConfigDir就是遍历下该目录中的文件,对文件排个序,而后调用ParseConfigFile.
bool Parser::ParseConfig(const std::string& path) { if (is_dir(path.c_str())) { return ParseConfigDir(path); } return ParseConfigFile(path); } 复制代码
而ParseConfigFile就是读取文件中的数据后,将数据传递给ParseData函数,最后遍历section_parsers_调用其EndFile函数, EndFile后面再分析,由于是多态实现,咱们先看看ParseData
bool Parser::ParseConfigFile(const std::string& path) { LOG(INFO) << "Parsing file " << path << "..."; Timer t; std::string data; if (!read_file(path, &data)) { //将数据读取到data return false; } data.push_back('\n'); // TODO: fix parse_config. ParseData(path, data); //解析数据 for (const auto& sp : section_parsers_) { sp.second->EndFile(path); } LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)"; return true; } 复制代码
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.
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()); //将data的内容复制到data_copy中 data_copy.push_back('\0'); //追加一个结束符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 (;;) { switch (next_token(&state)) { // 遍历data_copy中每个字符 case T_EOF: //若是是文件结尾,则调用EndSection if (section_parser) { section_parser->EndSection(); } return; case T_NEWLINE://读取了一行数据 state.line++; if (args.empty()) { break; } /* * 1.section_parsers_是一个std:map * 2.C++中std:map的count函数是查找key,至关于Java中Map的contains * 3.section_parsers_中只有三个key,on service import,以前AddSectionParser函数加入 */ if (section_parsers_.count(args[0])) { //判断是否包含 on service import if (section_parser) { section_parser->EndSection(); } section_parser = section_parsers_[args[0]].get();//取出对应的parser std::string ret_err; if (!section_parser->ParseSection(args, &ret_err)) {//解析对应的Section parse_error(&state, "%s\n", ret_err.c_str()); section_parser = nullptr; } } else if (section_parser) { //不包含 on service import则是command或option std::string ret_err; if (!section_parser->ParseLineSection(args, state.filename, state.line, &ret_err)) {//解析command或option parse_error(&state, "%s\n", ret_err.c_str()); } } args.clear(); break; case T_TEXT: //将读取的一行数据放到args中,args以空格或""做为分割,将一行数据拆分红单词放进数组中 args.emplace_back(state.text); break; } } } 复制代码
这里其实涉及到on service import对应的三个解析器ActionParser,ServiceParser,ImportParser,它们是在以前加入到section_parsers_这个map中的
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>()); void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) { section_parsers_[name] = std::move(parser); } 复制代码
它们都是SectionParser的子类,SectionParser有四个纯虚函数,分别是ParseSection、ParseLineSection、EndSection,EndFile.
class SectionParser { public: virtual ~SectionParser() { } /* * 1.C++中纯虚函数的定义格式是 virtual做为修饰符,而后赋值给0,至关于Java中的抽象方法 * 2.若是不赋值给0,却以virtual做为修饰符,这种是虚函数,虚函数能够有方法体,至关于Java中父类的方法,主要用于子类的重载 * 3.只要包含纯虚函数的类就是抽象类,不能new,只能经过子类实现,这个跟Java同样 */ virtual bool ParseSection(const std::vector<std::string>& args, std::string* err) = 0; virtual bool ParseLineSection(const std::vector<std::string>& args, const std::string& filename, int line, std::string* err) const = 0; virtual void EndSection() = 0; virtual void EndFile(const std::string& filename) = 0; }; 复制代码
接下来我将分析这三个Perser的ParseSection、ParseLineSection、EndSection,EndFile具体实现
定义在platform/system/core/init/action.cpp
咱们先看ParseSection,它先将args中下标1到结尾的数据复制到triggers数组中,而后是构建Action对象,调用InitTriggers,解析这些trigger
bool ActionParser::ParseSection(const std::vector<std::string>& args, std::string* err) { std::vector<std::string> triggers(args.begin() + 1, args.end()); //将args复制到triggers中,除去下标0 if (triggers.size() < 1) { *err = "actions must have a trigger"; return false; } auto action = std::make_unique<Action>(false); if (!action->InitTriggers(triggers, err)) { //调用InitTriggers解析trigger return false; } action_ = std::move(action); return true; } 复制代码
InitTriggers经过比较是否以"property:"开头,区分trigger的类型,若是是property trigger,就调用ParsePropertyTrigger, 若是是event trigger,就将args的参数赋值给event_trigger_,类型是string
bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) { const static std::string prop_str("property:"); for (std::size_t i = 0; i < args.size(); ++i) { ... if (!args[i].compare(0, prop_str.length(), prop_str)) { if (!ParsePropertyTrigger(args[i], err)) { return false; } } else { ... event_trigger_ = args[i]; } } return true; } 复制代码
ParsePropertyTrigger函数先是将字符以"="分割为name-value,而后将name-value存入property_triggers_这个map中
bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length())); //截取property:后的内容
size_t equal_pos = prop_name.find('=');
if (equal_pos == std::string::npos) {
*err = "property trigger found without matching '='";
return false;
}
std::string prop_value(prop_name.substr(equal_pos + 1)); //取出value
prop_name.erase(equal_pos); //删除下标为equal_pos的字符,也就是删除"="
if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
//将name-value存放到map中,emplace至关于put操做
*err = "multiple property triggers found for same property";
return false;
}
return true;
}
复制代码
从上面看出,ParseSection函数的做用就是构造一个Action对象,将trigger条件记录到Action这个对象中,若是是event trigger就赋值给event_trigger_, 若是是property trigger就存放到property_triggers_这个map中. 接下来咱们分析ParseLineSection
ParseLineSection是直接调用Action对象的AddCommand函数
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; } 复制代码
AddCommand看名字就大概知道是添加命令,它首先是作一些参数空值的检查,而后是调用FindFunction查找命令对应的执行函数, 最后将这些信息包装成Command对象存放到commands_数组中,这里比较关键的就是FindFunction
bool Action::AddCommand(const std::vector<std::string>& args, const std::string& filename, int line, std::string* err) { ... //一些参数检查 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);//commands_是个数组,emplace_back就至关于add } 复制代码
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是一个结构体,里面有三个值,第一个是参数最小数目,第二个是参数最大数目,第三个就是执行函数, 以后做了参数的数目检查,也就是说命令后的参数要在最小值和最大值之间.
const Function FindFunction(const std::string& keyword, size_t num_args, std::string* err) const { using android::base::StringPrintf; auto function_info_it = map().find(keyword); //找到keyword对应的entry if (function_info_it == map().end()) { // end是最后一个元素后的元素,表示找不到 *err = StringPrintf("invalid keyword '%s'", keyword.c_str()); return nullptr; } auto function_info = function_info_it->second;//获取value auto min_args = std::get<0>(function_info);//获取参数数量最小值 auto max_args = std::get<1>(function_info);//获取参数数量最大值 if (min_args == max_args && num_args != min_args) {//将实际参数数量与最大值最小值比较 *err = StringPrintf("%s requires %zu argument%s", keyword.c_str(), min_args, (min_args > 1 || min_args == 0) ? "s" : ""); return nullptr; } if (num_args < min_args || num_args > max_args) { if (max_args == std::numeric_limits<decltype(max_args)>::max()) { *err = StringPrintf("%s requires at least %zu argument%s", keyword.c_str(), min_args, min_args > 1 ? "s" : ""); } else { *err = StringPrintf("%s requires between %zu and %zu arguments", keyword.c_str(), min_args, max_args); } return nullptr; } return std::get<Function>(function_info);//返回命令对应的执行函数 } 复制代码
咱们看看map()的实现,定义在platform/system/core/init/builtins.cpp
这个实现比较简单,就是直接构造一个map,而后返回. 好比{"bootchart", {1,1,do_bootchart}}, 表示命令名称叫bootchart,对应的执行函数是do_bootchart,容许传入的最小和最大参数数量是1
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); //表示size_t的最大值 // clang-format off static const Map builtin_functions = { {"bootchart", {1, 1, do_bootchart}}, {"chmod", {2, 2, do_chmod}}, {"chown", {2, 3, do_chown}}, {"class_reset", {1, 1, do_class_reset}}, {"class_restart", {1, 1, do_class_restart}}, {"class_start", {1, 1, do_class_start}}, {"class_stop", {1, 1, do_class_stop}}, {"copy", {2, 2, do_copy}}, {"domainname", {1, 1, do_domainname}}, {"enable", {1, 1, do_enable}}, {"exec", {1, kMax, do_exec}}, {"exec_start", {1, 1, do_exec_start}}, {"export", {2, 2, do_export}}, {"hostname", {1, 1, do_hostname}}, {"ifup", {1, 1, do_ifup}}, {"init_user0", {0, 0, do_init_user0}}, {"insmod", {1, kMax, do_insmod}}, {"installkey", {1, 1, do_installkey}}, {"load_persist_props", {0, 0, do_load_persist_props}}, {"load_system_props", {0, 0, do_load_system_props}}, {"loglevel", {1, 1, do_loglevel}}, {"mkdir", {1, 4, do_mkdir}}, {"mount_all", {1, kMax, do_mount_all}}, {"mount", {3, kMax, do_mount}}, {"umount", {1, 1, do_umount}}, {"restart", {1, 1, do_restart}}, {"restorecon", {1, kMax, do_restorecon}}, {"restorecon_recursive", {1, kMax, do_restorecon_recursive}}, {"rm", {1, 1, do_rm}}, {"rmdir", {1, 1, do_rmdir}}, {"setprop", {2, 2, do_setprop}}, {"setrlimit", {3, 3, do_setrlimit}}, {"start", {1, 1, do_start}}, {"stop", {1, 1, do_stop}}, {"swapon_all", {1, 1, do_swapon_all}}, {"symlink", {2, 2, do_symlink}}, {"sysclktz", {1, 1, do_sysclktz}}, {"trigger", {1, 1, do_trigger}}, {"verity_load_state", {0, 0, do_verity_load_state}}, {"verity_update_state", {0, 0, do_verity_update_state}}, {"wait", {1, 2, do_wait}}, {"wait_for_prop", {2, 2, do_wait_for_prop}}, {"write", {2, 2, do_write}}, }; // clang-format on return builtin_functions; } 复制代码
接下来咱们看看EndSection,直接是调用ActionManager::GetInstance().AddAction
void ActionParser::EndSection() { if (action_ && action_->NumCommands() > 0) { ActionManager::GetInstance().AddAction(std::move(action_)); } } 复制代码
AddAction首先是查找是否有存在的同名Action,若是有就将他们的命令合并,没有就将它存入数组actions_中
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); });//find_if是集合中用于比较的模板,上面这种写法是lambda表达式 if (old_action_it != actions_.end()) {//在数组actions中找到Action说明已经存在同名,就合并command (*old_action_it)->CombineAction(*action); } else { //找不到就加入数组 actions_.emplace_back(std::move(action)); } } bool Action::TriggersEqual(const Action& other) const { return property_triggers_ == other.property_triggers_ && event_trigger_ == other.event_trigger_;//比较以前记录的event trigger和property trigger } void Action::CombineAction(const Action& action) { for (const auto& c : action.commands_) { //将新的Action中的command合并到老的Action commands_.emplace_back(c); } } 复制代码
EndFile是一个空实现,定义在platform/system/core/init/action.h
class ActionParser : public SectionParser { public: ActionParser() : action_(nullptr) { } bool ParseSection(const std::vector<std::string>& args, std::string* err) override; bool ParseLineSection(const std::vector<std::string>& args, const std::string& filename, int line, std::string* err) const override; void EndSection() override; void EndFile(const std::string&) override { //空实现 } private: std::unique_ptr<Action> action_; }; 复制代码
讲了这么多,小结一下ActionParser作的事情. 它有三个重要的重载函数,ParseSection、ParseLineSection、EndSection.
定义在platform/system/core/init/service.cpp
咱们仍是分析它的四个函数ParseSection、ParseLineSection、EndSection、EndFile
ParseSection首先是判断单词个数至少有三个,由于必须有一个服务名称和执行文件,而后是判断名称是否合法,主要是一些长度及内容的检查,最后就是构造一个Service对象
bool ServiceParser::ParseSection(const std::vector<std::string>& args, std::string* err) { if (args.size() < 3) { // 传入单词个数至少三个 *err = "services must have a name and a program"; return false; } const std::string& name = args[1]; if (!IsValidName(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, str_args);// 构造Service对象 return true; } 复制代码
ParseLineSection直接执行Service的ParseLine函数
bool ServiceParser::ParseLineSection(const std::vector<std::string>& args, const std::string& filename, int line, std::string* err) const { return service_ ? service_->ParseLine(args, err) : false; } 复制代码
ParseLine的思路跟以前Action同样,就是根据option名称从map中找到对应的执行函数,而后执行这个函数. 这些执行函数主要做用就是对传入参数作一些处理,而后将信息记录到Service对象中
bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) { if (args.empty()) { *err = "option needed, but not provided"; return false; } static const OptionParserMap parser_map; auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);//从map中找出执行函数 if (!parser) { return false; } return (this->*parser)(args, err);//执行找到的这个函数 } 复制代码
map()返回的map以下,定义在定义在platform/system/core/init/service.cpp中
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}}, {"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}}, {"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}}, {"oom_score_adjust", {1, 1, &Service::ParseOomScoreAdjust}}, {"namespace", {1, 2, &Service::ParseNamespace}}, {"seclabel", {1, 1, &Service::ParseSeclabel}}, {"setenv", {2, 2, &Service::ParseSetenv}}, {"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; } 复制代码
接下来咱们看看EndSection,直接调用ServiceManager的AddService函数
void ServiceParser::EndSection() { if (service_) { ServiceManager::GetInstance().AddService(std::move(service_)); } } 复制代码
AddService的实现比较简单,就是经过比较service的name,查看存放Service的数组services_中是否有同名的service,若是有就打印下错误日志,直接返回, 若是不存在就加入数组中
void ServiceManager::AddService(std::unique_ptr<Service> service) { Service* old_service = FindServiceByName(service->name()); //查找services_中是否已存在同名service if (old_service) { LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'"; return; } services_.emplace_back(std::move(service));//加入数组 } Service* ServiceManager::FindServiceByName(const std::string& name) const { auto svc = std::find_if(services_.begin(), services_.end(), [&name] (const std::unique_ptr<Service>& s) { return name == s->name(); });//跟以前action同样,遍历数组进行比较,查找同名service if (svc != services_.end()) { return svc->get(); //找到就返回service } return nullptr; } 复制代码
EndFile依然是一个空实现,定义在platform/system/core/init/service.h
class ServiceParser : public SectionParser { public: ServiceParser() : service_(nullptr) { } bool ParseSection(const std::vector<std::string>& args, std::string* err) override; bool ParseLineSection(const std::vector<std::string>& args, const std::string& filename, int line, std::string* err) const override; void EndSection() override; void EndFile(const std::string&) override { //空实现 } private: bool IsValidName(const std::string& name) const; std::unique_ptr<Service> service_; }; 复制代码
从上面能够看出,ServiceParser的处理跟ActionParser差很少,区别在于Action将执行函数存起来等待Trigger触发时执行,Service找到执行函数后是立刻执行
定义在platform/system/core/init/import_parser.cpp
最后咱们看看ImportParser,ImportParser的ParseLineSection、EndSection都是空实现,只实现了ParseSection和EndFile, 由于它的语法比较单一,只有一行. 咱们来看看它的ParseSection函数
首先检查单词只能是两个,由于只能是import xxx 这种语法,而后调用expand_props处理下参数,最后将结果放入数组imports_存起来
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); //处理第二个参数 if (!ret) { *err = "error while expanding import"; return false; } LOG(INFO) << "Added '" << conf_file << "' to import list"; imports_.emplace_back(std::move(conf_file)); //存入数组 return true; } 复制代码
expand_props 定义在platform/system/core/init/util.cpp ,主要做用就是找到x.y这种语法,将x.y取出来做为name,去属性系统中找对应的value,而后替换
bool expand_props(const std::string& src, std::string* dst) { const char* src_ptr = src.c_str(); if (!dst) { return false; } /* - variables can either be $x.y or ${x.y}, in case they are only part * of the string. * - will accept ? as a literal $. * - no nested property expansion, i.e. ${foo.${bar}} is not supported, * bad things will happen * - ${x.y:-default} will return default value if property empty. */ //这段英文大概的意思是 参数要么是$x.y,要么是${x.y},它们都是路径的一部分,?表示字符 $ , //${foo.${bar}}这种递归写法是不支持的,由于会发生一些糟糕的事情 //${x.y:-default}会将default做为默认值返回,若是找不到对应的属性值的话 while (*src_ptr) { const char* c; c = strchr(src_ptr, '$'); if (!c) { // 找不到$符号,直接将dst赋值为src返回 dst->append(src_ptr); return true; } dst->append(src_ptr, c); c++; if (*c == '$') { //跳过$ dst->push_back(*(c++)); src_ptr = c; continue; } else if (*c == '\0') { return true; } std::string prop_name; std::string def_val; if (*c == '{') { //找到 { 就准备找 }的下标,而后截取它们之间的字符串,对应${x.y}的状况 c++; const char* end = strchr(c, '}'); if (!end) { // failed to find closing brace, abort. LOG(ERROR) << "unexpected end of string in '" << src << "', looking for }"; return false; } prop_name = std::string(c, end); //截取{}之间的字符串做为name c = end + 1; size_t def = prop_name.find(":-"); //若是发现有 ":-" ,就将后面的值做为默认值先存起来 if (def < prop_name.size()) { def_val = prop_name.substr(def + 2); prop_name = prop_name.substr(0, def); } } else { //对应$x.y的状况 prop_name = c; LOG(ERROR) << "using deprecated syntax for specifying property '" << c << "', use ${name} instead"; c += prop_name.size(); } if (prop_name.empty()) { LOG(ERROR) << "invalid zero-length property name in '" << src << "'"; return false; } std::string prop_val = android::base::GetProperty(prop_name, ""); //经过name在属性系统中找对应的value,内部调用的是以前属性系统的__system_property_find函数 if (prop_val.empty()) { //没有找到值就返回默认值 if (def_val.empty()) { LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'"; return false; } prop_val = def_val; } dst->append(prop_val); src_ptr = c; } return true; } 复制代码
EndFile的实现比较简单,就是复制下ParseSection函数解析的.rc文件数组,而后遍历数组,调用最开始的ParseConfig函数解析一个完整的路径
void ImportParser::EndFile(const std::string& filename) { auto current_imports = std::move(imports_); imports_.clear(); for (const auto& s : current_imports) { if (!Parser::GetInstance().ParseConfig(s)) { PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'"; } } } 复制代码
由此,咱们将Android Init Language语法的转化过程分析完毕,其实它们核心的解析器就三个,ActionParser,ServiceParser,ImportParser. 而这几个解析器主要是实现ParseSection、ParseLineSection、EndSection、EndFile四个函数
on early service ueventd /sbin/ueventd import /init.${ro.zygote}.rc 复制代码
write /proc/1/oom_score_adj -1000 class core 复制代码
通过上一步的解析,系统从各类.rc文件中读取了须要执行的Action和Service,可是仍是须要一些额外的配置,也须要加入触发条件准备去触发
// 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) parser.DumpState(); //打印一些当前Parser的信息,默认是不执行的 ActionManager& am = ActionManager::GetInstance(); am.QueueEventTrigger("early-init");//QueueEventTrigger用于触发Action,这里触发 early-init事件 // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev... am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done"); //QueueBuiltinAction用于添加Action,第一个参数是Action要执行的Command,第二个是Trigger // ... so that we can start queuing up actions that require stuff from /dev. am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits"); am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict"); am.QueueBuiltinAction(keychord_init_action, "keychord_init"); am.QueueBuiltinAction(console_init_action, "console_init"); // Trigger all the boot actions to get us started. am.QueueEventTrigger("init"); // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random // wasn't ready immediately after wait_for_coldboot_done am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); // Don't mount filesystems or start core system services in charger mode. std::string bootmode = GetProperty("ro.bootmode", ""); if (bootmode == "charger") { am.QueueEventTrigger("charger"); } else { am.QueueEventTrigger("late-init"); } // Run all property triggers based on current state of the properties. am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers"); 复制代码
定义在platform/system/core/init/action.cpp
它并无去触发trigger,而是构造了一个EventTrigger对象,放到队列中存起来
void ActionManager::QueueEventTrigger(const std::string& trigger) {
trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
}
class EventTrigger : public Trigger {
public:
explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
}
bool CheckTriggers(const Action& action) const override {
return action.CheckEventTrigger(trigger_);
}
private:
const std::string trigger_;
};
复制代码
定义在platform/system/core/init/action.cpp
这个函数有两个参数,第一个参数是一个函数指针,第二参数是字符串. 首先是建立一个Action对象,将第二参数做为Action触发条件, 将第一个参数做为Action触发后的执行命令,而且又把第二个参数做为命令的参数,最后是将Action加入触发队列并加入Action列表
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) { auto action = std::make_unique<Action>(true); std::vector<std::string> name_vector{name}; if (!action->InitSingleTrigger(name)) { //调用InitTriggers,以前讲过用于将name加入Action的trigger列表 return; } action->AddCommand(func, name_vector);//加入Action的command列表 trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));//将Action加入触发队列 actions_.emplace_back(std::move(action));//加入Action列表 } 复制代码
以前的全部工做都是往各类数组、队列里面存入信息,并无真正去触发,而接下来的工做就是真正去触发这些事件,以及用epoll不断监听新的事件
while (true) { // By default, sleep until something happens. int epoll_timeout_ms = -1; //epoll超时时间,至关于阻塞时间 /* * 1.waiting_for_prop和IsWaitingForExec都是判断一个Timer为不为空,至关于一个标志位 * 2.waiting_for_prop负责属性设置,IsWaitingForExe负责service运行 * 3.当有属性设置或Service开始运行时,这两个值就不为空,直到执行完毕才置为空 * 4.其实这两个判断条件主要做用就是保证属性设置和service启动的完整性,也能够说是为了同步 */ if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) { am.ExecuteOneCommand(); //执行一个command } if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) { restart_processes(); //重启服务 // If there's a process that needs restarting, wake up in time for that. if (process_needs_restart_at != 0) { //当有进程须要重启时,设置epoll_timeout_ms为重启等待时间 epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000; if (epoll_timeout_ms < 0) epoll_timeout_ms = 0; } // If there's more work to do, wake up again immediately. if (am.HasMoreCommands()) epoll_timeout_ms = 0; //当还有命令要执行时,将epoll_timeout_ms设置为0 } epoll_event ev; /* * 1.epoll_wait与上一篇中讲的epoll_create一、epoll_ctl是一块儿使用的 * 2.epoll_create1用于建立epoll的文件描述符,epoll_ctl、epoll_wait都把它建立的fd做为第一个参数传入 * 3.epoll_ctl用于操做epoll,EPOLL_CTL_ADD:注册新的fd到epfd中,EPOLL_CTL_MOD:修改已经注册的fd的监听事件,EPOLL_CTL_DEL:从epfd中删除一个fd; * 4.epoll_wait用于等待事件的产生,epoll_ctl调用EPOLL_CTL_ADD时会传入须要监听什么类型的事件, * 好比EPOLLIN表示监听fd可读,当该fd有可读的数据时,调用epoll_wait通过epoll_timeout_ms时间就会把该事件的信息返回给&ev */ int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms)); if (nr == -1) { PLOG(ERROR) << "epoll_wait failed"; } else if (nr == 1) { ((void (*)()) ev.data.ptr)();//当有event返回时,取出ev.data.ptr(以前epoll_ctl注册时的回调函数),直接执行 //上一篇中在signal_handler_init和start_property_service有注册两个fd的监听,一个用于监听SIGCHLD(子进程结束信号),一个用于监听属性设置 } } return 0; } 复制代码
定义在platform/system/core/init/action.cpp
从名字能够看出,它只执行一个command,是的,只执行一个. 在函数一开始就从trigger_queue_队列中取出一个trigger, 而后遍历全部action,找出知足trigger条件的action加入待执行列表current_executing_actions_中, 接着从这个列表中取出一个action,执行它的第一个命令,并将命令所在下标自加1. 因为ExecuteOneCommand外部是一个无限循环, 所以按照上面的逻辑一遍遍执行,将按照trigger表的顺序,依次执行知足trigger条件的action,而后依次执行action中的命令.
void ActionManager::ExecuteOneCommand() { // Loop through the trigger queue until we have an action to execute while (current_executing_actions_.empty() && !trigger_queue_.empty()) {//current_executing_actions_.empty保证了一次只遍历一个trigger for (const auto& action : actions_) {//遍历全部的Action if (trigger_queue_.front()->CheckTriggers(*action)) {//知足当前Trigger条件的就加入队列current_executing_actions_ current_executing_actions_.emplace(action.get()); } } trigger_queue_.pop();//从trigger_queue_中踢除一个trigger } if (current_executing_actions_.empty()) { return; } auto action = current_executing_actions_.front();//从知足trigger条件的action队列中取出一个action if (current_command_ == 0) { std::string trigger_name = action->BuildTriggersString(); LOG(INFO) << "processing action (" << trigger_name << ")"; } action->ExecuteOneCommand(current_command_);//执行该action中的第current_command_个命令 // If this was the last command in the current action, then remove // the action from the executing list. // If this action was oneshot, then also remove it from actions_. ++current_command_; //下标加1 if (current_command_ == action->NumCommands()) { //若是是最后一条命令 current_executing_actions_.pop();//将该action从current_executing_actions_中踢除 current_command_ = 0; if (action->oneshot()) {//若是action只执行一次,将该action从数组actions_中踢除 auto eraser = [&action] (std::unique_ptr<Action>& a) { return a.get() == action; }; actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser)); } } } 复制代码
定义在platform/system/core/init/init.cpp
restart_processes调用的实际上是ForEachServiceWithFlags函数,这个函数主要是遍历services_数组,比较它们的flags是不是SVC_RESTARTING, 也就是当前service是不是等待重启的,若是是就执行它的RestartIfNeeded函数
static void restart_processes() { process_needs_restart_at = 0; ServiceManager::GetInstance().ForEachServiceWithFlags(SVC_RESTARTING, [](Service* s) { s->RestartIfNeeded(&process_needs_restart_at); }); } void ServiceManager::ForEachServiceWithFlags(unsigned matchflags, void (*func)(Service* svc)) const { for (const auto& s : services_) { //遍历全部service if (s->flags() & matchflags) {//找出flags是SVC_RESTARTING的,执行func,也就是传入的RestartIfNeeded func(s.get()); } } } 复制代码
定义在platform/system/core/init/service.cpp
这个函数将主要工做交给了Start,也就是具体的启动service,可是交给它以前作了一些判断,也就是5秒内只能启动一个服务, 若是有多个服务,那么后续的服务将进入等待
void Service::RestartIfNeeded(time_t* process_needs_restart_at) { boot_clock::time_point now = boot_clock::now(); boot_clock::time_point next_start = time_started_ + 5s; //time_started_是上一个service启动的时间戳 if (now > next_start) { //也就是说两个服务进程启动的间隔必须大于5s flags_ &= (~SVC_RESTARTING); // &= 加 ~ 至关于取消标记 Start(); return; } time_t next_start_time_t = time(nullptr) + time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count()); if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) { *process_needs_restart_at = next_start_time_t;//若是两个service启动间隔小于5s,将剩余时间赋值给process_needs_restart_at } } 复制代码
定义在platform/system/core/init/service.cpp
Start是具体去启动服务了,它主要是调用clone或fork建立子进程,而后调用execve执行配置的二进制文件,另外根据以前在.rc文件中的配置,去执行这些配置
bool Service::Start() { ... //清空标记,根据service的配置初始化console、SELinux策略等 LOG(INFO) << "starting service '" << name_ << "'..."; pid_t pid = -1; if (namespace_flags_) {//这个标记当service定义了namespace时会赋值为CLONE_NEWPID|CLONE_NEWNS pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr); //以clone方式在新的namespace建立子进程 } else { pid = fork();//以fork方式建立子进程 } if (pid == 0) {//表示建立子进程成功 ... //执行service配置的其余参数,好比setenv、writepid等 std::vector<char*> strs; ExpandArgs(args_, &strs);//将args_解析一下,好比有${x.y},而后赋值表strs if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) { //执行系统调用execve,也就是执行配置的二进制文件,把参数传进去 PLOG(ERROR) << "cannot execve('" << strs[0] << "')"; } _exit(127); } if (pid < 0) { //子进程建立失败 PLOG(ERROR) << "failed to fork for '" << name_ << "'"; pid_ = 0; return false; } ... //执行service其余参数如oom_score_adjust_,改变service运行状态等 } 复制代码
小结
这一阶段Init进程作了许多重要的事情,好比解析.rc文件,这里配置了全部须要执行的action和须要启动的service, Init进程根据语法一步步去解析.rc,将这些配置转换成一个个数组、队列,而后开启无限循环去处理这些数组、队列中的command和service,而且经过epoll监听子进程结束和属性设置.
至此,我已经将Init进程的三个阶段讲解完了,下一篇我将讲解.rc中配置的一个重要的service--zygote,它是咱们app程序的鼻祖.