Android Initial Language
1 概述
Android Init Language,简称AIL,即安卓初始化语言。Android init进程解析的扩展名为rc文件就是用这种语言写的。
所有语法规则都可以在readme.txt文件中获取。本文基于Android N的readme文件进行讲解,基本是对此文件的翻译,感觉翻译不到位的,可以查看原英文文件,路径如下:
android/system/core/init/readme.txt
2 语法
安卓初始化语言由五大类语句组成,分别是Actions、Commands、Services、Options和Imports。
所有这些都是以行为单位,各种记号由空格来隔开。C语言风格的反斜杠号可用于在记号间插入空格。双引号也可用于防止字符串被空格分割成多个记号。行末的反斜杠用于折行。
注释行以井号(#)开头(允许以空格开头)。
Actions和Services声明一个新的分组。所有的命令或选项都属于最近申明的分组。位于第一个分组之前的命令或选项将会被忽略。 Actions和Services有唯一的名字。如果Action出现重名,第二个Action的commands将被加在第一个的后面。如果Services出现重名,第二个将被忽略,并且会打印一条错误日志。
2.1 Actions
Actions其实就是一序列的Commands(命令)。Actions都有一个trigger(触发器),它被用于决定action的执行时间。当一个符合action触发条件的事件发生时,action会被加入到执行队列的末尾,除非它已经在队列里了。
队列中的每一个action都被依次提取出,而这个action中的每个command(命令)都将被依次执行。Init在这些命令的执行期间还控制着其他的活动(设备节点的创建和注销、属性的设置、进程的重启)。
Actions格式如下:
2.2 Triggers
触发器本质上是一个字符串,能够匹配某种包含该字符串的事件。触发器又被细分为事件触发器(event trigger)和属性触发器(property trigger)。触发器是一个用于匹配特定事件类型的字符串,用于是Actions发生。
事件触发器可由"trigger"命令触发,或初始化过程中通过QueueEventTrigger()触发,通常是一些事先定义的简单字符串,例如:boot,late-init。
属性触发器是当指定属性的变量值变成指定值时触发,其格式为'property:<name>=<value>' and 'property:<name>=*'。
一个Action可以有多个属性触发器,但是最多有一个事件触发器.
下面我们看两个例子:
on boot && property:a=b
该Action只有在boot事件发生时,并且属性a和b相等的情况下才会被触发.
on property:a=b && property:c=d
该Action会在以下三种情况被触发:
1)在启动时,如果属性a的值等于b并且属性c的值等于d
2)在属性c的值已经是d的情况下,属性a的值被更新为b
3)在属性a的值已经是b的情况下,属性c的值被更新为d
触发器分为定义好的触发器和设置的触发器。
2.2.1 定义好的触发器
定义好的事件触发器有early-init\init\late-init,是在初始化过程中通过QueueEventTrigger()触发。
early-init
一些前置工作,创建mnt目录,挂载tmpfs等。
init
设置loglevel,建立符号链接,创建system/data/cache目录,proc/dev处理等。
late-init
触发用户设置的触发器。
2.2.2 创建的触发器
用户创建的触发器,通过2.2.1中某一个阶段的trigger命令触发。
trigger <用户创建的触发器名称>
触发某一触发器。
2.3 Commands
command有很多,请查阅readme.txt文件。
2.4 Services
Init启动的程序,可以选择要不要在退出后重启。格式如下:
2.5 Options
Options是服务的修饰符,他们影响Init运行服务的方式和时间。
Critical
这是一项设备关键型服务。 如果它在四分钟内退出超过四次以上,设备将重启进入恢复模式。
Class <name>
指定服务的类名。 命名类中的所有服务可以一起启动或停止。 如果未通过class选项指定服务,则服务在类“default”中。
Seclabel <seclabel>
在执行此服务之前更改为“seclabel”。 主要供从rootfs运行的服务使用,例如 ueventd,adbd。 系统分区上的服务可以基于其文件安全上下文使用策略定义的转换。
如果未指定且策略中未定义转换,则默认为init上下文。
group <groupname> [ <groupname> ]*
在执行此服务之前更改为groupname。 除了(必需的)第一个之外的其他组名用于设置过程的补充组(通过setgroups())。 目前默认为root。
User <username>
在执行此服务前更改为username。
Oneshot
服务退出后,不进行重启。
onrestart <xxxx>
当服务重启的时候,执行一条指令。
Disabled
此服务不会自动从其类开始。 它必须通过名称显式启动。
socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
创建名为/ dev / socket / <name>的unix域套接字,并将其fd传递给已启动的进程。 <type>必须是“dgram”,“stream”或“seqpacket”。 用户和组默认为0.'seclabel'是套接字的SELinux安全上下文。 它默认为服务安全性上下文,由seclabel指定或基于服务可执行文件安全性上下文计算。
setenv <name> <value>
在启动的进程中将环境变量<name>设置为<value>。
ioprio <rt|be|idle> <0-7>
设置IO优先级。
writepid <file...>
当进程创建的时候,把进程ID写到给定的文件里面。意味着cgroup / cpuset的使用。
2.6 Imports
import关键字不是一个命令,而是一个部分,并在包含它的.rc文件完成解析后立即处理。
import <path>
解析init配置文件,扩展当前配置。 如果<path>是目录,则目录中的每个文件都将被解析为配置文件。 它不是递归的,嵌套目录不会被解析。
init可以执行imports的rc文件只有两次:
1)在初始引导期间导入/init.rc时
2)在mount_all期间在指定路径导入/ {system,vendor,odm} / etc / init /或.rc文件时
2.7 Properties
Init通过以下属性提供有关其负责的服务的信息。
init.svc.<name>
命名服务的状态("stopped", "stopping", "running", "restarting")。
2.8 还有介绍systrace和bootchat,感兴趣可以研究一下。
3 rc文件
init语言用于带.rc文件扩展名的纯文本文件中。 这些通常是系统中多个位置中的多个,如下所述。
/init.rc是主.rc文件,在执行开始时由init可执行文件加载。 它负责系统的初始设置。 它导入/init.${ro.hardware}.rc这是主要供应商提供的.rc文件。
在mount_all命令期间,init可执行文件加载/ {system,vendor,odm} / etc / init /目录中包含的所有文件。 在文件系统挂载后,这些目录包含所有操作和服务。
可以在mount_all命令行中指定路径,使其在指定的路径中导入.rc文件,而不是上面列出的默认路径。 这主要用于支持工厂模式和其他非标准引导模式。 三个默认路径一般用于正常引导过程。
下面是这些路径的意图:
1)/system/etc/init/放置SurfaceFlinger\MediaService\logcatd等核心系统条目。
2)/vendor/etc/init/放置芯片方案商的soc核心功能需要的actions和daemons。
3)/odm/etc/init/用于设备制造商,例如运动传感器或其他外围功能所需的操作或守护程序。
所有Services应该有service条目在相应的init.rc文件中,这些服务的二进制文件在system、vendor、odm分区,rc文件在对应分区的/etc/init/目录。每个init .rc文件还应包含与其服务关联的任何操作。
示例:位于system/core/logcat/目录中的logcatd.rc和Android.mk文件。 Android.mk文件中的LOCAL_INIT_RC宏在构建过程中将logcatd.rc放在/system/etc/init /中。 Init在mount_all命令期间加载logcatd.rc并允许运行服务并在适当时是actions如队列。
根据其守护进程分解init .rc文件比以前使用的单片init .rc文件更受欢迎。 这种方法可确保init读取的唯一服务条目和init执行的唯一操作对应于其二进制文件实际存在于文件系统上的服务,而单片init .rc文件则不然。 当将多个服务添加到系统时,这另外将有助于合并冲突解决,因为每个服务将进入单独的文件。