上一篇我们知道init.rc在core/init/init.cpp,也就是init进程的SecondStageMain阶段解析和执行的。本篇我们看下执行的具体过程。本文基于内核版本kernel/arm64 - android-amber-intel-linux--android11分支。
为了更好理解对rc文件的执行过程,需要了解rc文件本身的语法和作用
一、init.rc文件语法说明(AIL语法)
rc文件使用的是Android Init Language (AIL)语言。
core/init/README.md对rc文件做了很好的介绍。
整理如下:
(或见https://android.googlesource.com/platform/system/core/+/master/init/README.md)
Android Init Language包含五类语句:
Actions, Commands, Services, Options, and Imports.
Action和Service是主要的任务执行单位。其隐式地声明了一个section,一个section下有属于它的commands和options。其中Service名称(name)唯一。如果一个Service声明了一个已存在的name,则被忽略。
- Actions:由一些列command指令组成。Action会由trigger或property值的匹配,来决定action何时被执行。当条件被触发,多个符合条件的action将被按声明顺序依次加入一个“执行队列①”中,并按顺序执行。每个action的command指令也是按顺序执行。
Action的格式:
on <trigger> [&& <trigger>]*
<command>
<command>
<command>
有时候在表述时,Action和trigger有时会混用,其实指的是一个事情。trigger指的是触发该action的条件,而action指的是触发该条件后,要执行的整个代码段。
- Triggers:用于匹配某些事件,从而触发一个Action。Triggers由event trigger和property trigger组成(比如上面Action的例子)。
event trigger可以由rc文件里的`trigger`命令触发,或者在代码里调用`QueueEventTrigger`函数触发。以一个简单的字符串来代表一个event trigger,例如“boot”或者“late-init”。
Property triggers是当某个property的值变为了所指定的值后触发。格式为'property:<name>=<value>'、'property:<name>=\*'
比如,在一个rc文件中作了如下声明:
on boot
setprop a 1
setprop b 2
on boot && property:true=true
setprop c 1
setprop d 2
on boot
setprop e 1
setprop f 2
当boot这个trigger触发了(在程序中通过代码调用,或者在rc中被其他action所触发),并且"true"(这是一个属性名)这个property等于"true"(属性的值), 则执行的结果为:
setprop a 1
setprop b 2
setprop c 1
setprop d 2
setprop e 1
setprop f 2
当boot触发了,但是"true"这个property不等于"true",那么执行结果为:
setprop a 1
setprop b 2
setprop e 1
setprop f 2
后期即使通过设置,使得property:true=true,该段代码也不会再执行了, 因为boot已经是一个执行过的event。
- Service:是由init进程来启动或restart(当他们退出了)的程序。格式如下:
service <name> <pathname> [ <argument> ]*
<option>
<option>
...
- Options
Options影响init进程在什么时候、何种方式来运行service
指令集太长,详细说明见README,这里以启动zygote里涉及到的几个为例感受下:
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 reserved_disk
socket zygote stream 660 root system
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
上面几个涉及到的options:
class:为service指定一个class名称。指定了同一class的service将会被一起start或者stop。如果未指定class选项,则service默认class为“default”。所有开机动画、关机动画锁需要的service都应该指定“animation”这个class。由于这些service启动得太早,将不被允许访问/data。
priority:设置该service的进程优先级。取值在-20~19之间。默认0。(通过 setpriority())
user:设置进程的user。默认root
group:设置进程的group(通过 setgroups())
socket:创建一个名为 /dev/socket/name 的domain socket(本地socket),并将fd传给当前进程。在启动service过程中同步创建。...
onrestart:在service restart时要执行的命令。
task_profiles:Set task profiles.即对进程的cgroup情况做相应设置。对task_profile的详细描述,见Cgroup 抽象层 | Android 开源项目 | Android Open Source Project
critical:指明本服务对设备来说是至关重要的。如果在window指定的分钟数(默认为4)内crash超过4次,则重启进入target指定模式(默认为bootloader,即fastboot)。
...
- 触发次序。init进程在early boot阶段会按如下次序触发相应trigger(称为build-in triggers),这些trigger是在init.cpp里通过QueueEventTrigger函数调用的:
`early-init`:第一个执行的trigger,在cgroups配置之后,但在ueventd冷启动完成之前触发。
`init` :在冷启动完成后触发
`charger`:如果`ro.bootmode == "charger"`触发
`late-init`:如果`ro.bootmode != "charger"或者在charging模式下由`healthd触发了boot时触发。
其他build-in trigger还有userspace-reboot-resume、userspace-reboot-requested、shutdown等
其他的triggers是在init.rc里配置的,称为非build-in triggers。默认要执行的序列在on late-init中指定
1. `early-fs` - Start vold.
2. `fs` - Vold is up. Mount partitions not marked as first-stage or latemounted.
3. `post-fs` - Configure anything dependent on early mounts.
4. `late-fs` - Mount partitions marked as latemounted.
5. `post-fs-data` - Mount and configure `/data`; set up encryption. `/metadata` is
reformatted here if it couldn't mount in first-stage init.
6. `zygote-start` - Start the zygote.
7. `early-boot` - After zygote has started.
8. `boot` - After `early-boot` actions have completed.
见init.rc内的相关内容:
# Mount filesystems and start core system services.
on late-init
trigger early-fs
# Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
# '--early' can be specified to skip entries with 'latemount'.
# /system and /vendor must be mounted by the end of the fs stage,
# while /data is optional.
trigger fs
trigger post-fs
# Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
# to only mount entries with 'latemount'. This is needed if '--early' is
# specified in the previous mount_all command on the fs stage.
# With /system mounted and properties form /system + /factory available,
# some services can be started.
trigger late-fs
# Now we can mount /data. File encryption requires keymaster to decrypt
# /data, which in turn can only be loaded when system properties are present.
trigger post-fs-data
# Should be before netd, but after apex, properties and logging is available.
trigger load_bpf_programs
# Now we can start zygote.
trigger zygote-start
# Remove a file to wake up anything waiting for firmware.
trigger firmware_mounts_complete
trigger early-boot
trigger boot
- Commands:一系列命令,可查阅Readme,不做赘述。
- Imports:引用其他rc文件。可以是绝对rc文件名,也可以是一个目录,当为目录是,会引入改目录下所有rc文件。但不支持嵌套的子目录。对于引用后的执行顺序,通过伪代码示例如下:
fn Import(file)
Parse(file)
for (import : file.imports)
Import(import)
Import(/system/etc/init/hw/init.rc)
Directories = [/system/etc/init, /system_ext/etc/init, /vendor/etc/init, /odm/etc/init, /product/etc/init]
for (directory : Directories)
files = <Alphabetical order of directory's contents>
for (file : files)
Import(file)
Action的执行顺序取决于它什么时候被解析(Parse)。从上面的逻辑看,本文件先被解析,再解析本文件所import的文件。也就是说,如果一个‘post-fs-data’ action被定义在主文件,同时主文件所引用(import)的rc文件中也有post-fs-data,那么执行顺序是主文件内的post-fs-data先执行,再执行import文件内的post-fs-data。
- Properties。见README
- 其他诸如Boot timing、Bootcharting、Systrace等用户记录启动过程信息。