<Android开发> Android内核系统开发-启动过程详解(第1部分 init.rc语法详解)
前言:
android系统开发有关代码详解流程等,均以高通8155为例,代码则是以对厂商开放的LA 1.1基线代码为例(基线代码等只有高通授权厂商才有权下载)。
Android设备的启动必须经历3个阶段过程,包含bootloader、Linux Kernel、和Android系统服务,每个阶段过程都有其对应的启动界面。由启动阶段过程可看出,Android系统实际上是运行在Linux内核上的一系列的“服务进程”,并不算一个完整意义上的“操作系统”。这些“服务进程”则是维持设备正常工作的关键所在,而这些进程的“始祖”就是init进程,init进程则是Linux kernel 启动的第一个进程。
init作为第一个被启动的进程,其进程PID值为0;进程init通过解析init.rc脚本来构建出系统的初始运行形态;android中的大多数系统服务都是由init.rc来启动的。所以init.rc的编写实现是每个厂商控制自己研发设备启动状态的利剑。
在详细熟悉init.rc的内容之前,先来看看".rc"文件书写的语法格式。所以本小节主要讲解 init.rc的语法 。
init.rc的语法
在高通的LA1.1基线代码中,对init.rc的注释比较少,在源码目录中有文档“system\core\init\README.md”对源码进行分析。
一个完整的init.rc脚本由4中类型的声明组成,即:
* Actions (动作)
* Commands (命令)
* Service (服务)
* Options (选项)
第一步,我们先来了解一些语法的通用规则方法:
》注释,以井号(“#”)开头;
》关键字和参数之间用空格分隔,语句的执行以行为单位;
》c语言风格的反斜杠转义字符(“\”)可以用来为参数添加空格;
》字符串中包含空格,可使用双引号括起来;
》行尾加反斜杠(“\”)表示当前行 与 下一行 合并为1行
》Actions(动作) 和 Server(服务)暗示着一个新语句的开始,这两个关键字后面跟着的Commands(命令)或者options(选项)都是是属于这个新语句的部分;
》Actions(动作) 和 Server(服务)有唯一的名字,如果出现和已有动作或服务重名的,将会被当成错误忽略掉;
下面详细分析各组成的元素…
1、Actions(动作)
动作的一般格式如下所示:
on <trigger> #触发的条件
<command 1> #在触发条件满足的条件下,执行该命令
<command 2> #在触发条件满足的条件下,执行该命令
<command 3> #在触发条件满足的条件下,执行该命令 可连续执行多个命令
.......
由上述可知,每一个Action其实是响应某事件的过程。即当所描述的触发事件产生时,依次执行各种command(命令)(同一个事件可以对应多个命令)。从系统源码实现的角度来说,当相应的事件发生后,系统会对init.rc中的各进行匹配,只要发现符合条件的Actions,就会把它加入“命令执行队列”的尾部(除非这个actions在队列中已经存在,存在则不重复加入),然后系统再对这些命令按顺序执行。
2、Commands(命令)
命令将在所属事件发生时被一个一个的执行。
init中定义的一些常见事件,如表2-1:
表:2-1 init.rc中常见的触发事件
Trigger | Description |
---|---|
boot | 这是init程序启动后触发的第一个事件 |
<name> | 当属性满足特定<vaule>时触发。 |
boot | 这是init程序启动后触发的第一个事件 |
device-added-<path> 或者 device-removed-<path> | 当设备节点添加/删除时触发此事件 |
service-exited-<name> | 当指定的服务存在时触发 |
针对上面所描述的事件,有如下表2-2所示命令可供使用。
表2-2 init.rc中常见命令
Command | Description |
---|---|
exec <path> [<argument>] * | Fork并执行一个程序,其路径为<path>。这条命令将阻塞直到该程序启动完成,因此它有可能造成init程序在某个点不停的等待 |
export <name><value> | 设置某个环境变量<name>的值为<value>。这对全局有效的,即其后所有进程都将继承这个变量 |
ifup <interface> | 使网络接口<interface>成功连接 |
import <filename> | 解析另一个配置文件,名为<filename>,以扩展当前配置 |
hostname <name> | 设置主机名为<name> |
chdir <directory> | 更改工作目录为<directory> |
chmod <octal-mode> <path> | 更改文件访问权限 |
chown<owne><group> <path> | 更改文件所有者和组群 |
chroot <directory> | 更改根目录位置 |
class_start<serviceclass> | 启动由<serviceclass>类指定的所有相关服务,如果它们不在运行状态的话 |
class_stop<serviceclass> | 停止由<serviceclass>类指定的服务,如果它们正在运行状态的话 |
domainname<name> | 设置域名 |
insmod<path> | 在<path>路径上安装一个模块 |
mkdir<path> [mode] [owner] [group] | 在<path>路径上新建一个目录 |
mount<type><device> <dir>[<mountoption>]* | 尝试在指定路径挂在一个设备 |
setkey | 目前未定义 |
setprop<name><value> | 设置系统属性<name>的值为<value> |
setlimit<resource><cur><max> | 设置一种资源的使用限制,这个概念亦存在与Linux系统中,<cur>表示软限制,<max>表示硬限制。更多详情请参考Linux资料 |
start<service> | 这个命令将启动一个服务,如果它没有处于运行状态的话 |
stop<service> | 这个命令将停止一个服务,如果它正处于运行状态的话 |
symlink<target><path> | 创建一个<path>路径的链接,目标为<target> |
syslktz<mins_west_of_gmt> | 设置基准时间,如果当前时间是GMT,这个值是0 |
trigger<event> | 触发一个事件 |
write<path><string> [<string>]* | 打开一个文件,并写入一个或多个字串 |
3、Services(服务)
Services其实就医一个可执行的程序,它们在特定选项的约束下会被init程序运行或者重启。Services可以在配置中指定是否需要退出时重启,这样当Services出现异常crash时就可以有机会复原。
Services的一般格式如下:
service <name><pathname> [<agument>]*
<option>
<option>
<option>
........
参数详解:
<name> :表示此service的名称;
<pathname>:此service所在的路径,因为时可执行文件,所以一定存在存储路径;
<agument>:启动service所带的参数;
<option>:对此service的约束选项(在后面讲解)
4、Options(选项)
在前面service中的选项有如下表4-1选项可选。
表4-1 service中的可用选项
Option | Description |
---|---|
criical | 表明这是设备至关重要的一个服务,如果它在4分钟内退出超过4次,则设备将重启进入恢复模式 |
disabled | 此服务不会自动启动,而是需要通过显示调用服务名来启动 |
setenv <name> <value> | 设置环境变量<name>为某个值<value> |
socket<name><type> <perm> [<user> [<group>] ] | 创建一个名为/dev/socket/<name>的unix domain socket,然后将它fd值传给启动它的进程。有效的<type> 值包括dgram,stream和seqpacket。而user和group的默认值是0 |
user<username> | 在启动服务前将用户切换至<username>,默认情况下用户都是root |
group<groupname> | 在启动服务前将用户组切换至<groupname> |
oneshot | 当此服务退出时,不要主动去重启它 |
class <name> | 为该服务指定一个class名,同一个class的所有服务必须同时启动或者停止。默认情况下服务的class名是“default”。 |
onrestart | 当此服务重启时,执行某些命令 |
priority<priority> | 设置服务进程的优先级.优先级取值范围为-20~19,默认是0.可以通过setpriority()设置 |
shutdown <shutdown_behavior> | 设置服务进程的关闭行为。如果未指定此选项,则在关闭过程中通过使用SIGTERM和SIGKILL终止服务。shutdown_behavior为"critical"时,服务在关闭期间不会被杀死,直到关闭超时为止。当服务进程关闭超时时,即使标记为"shutdown critical"的服务也将被杀死。当shutdown行为启动时,标有"shutdown critical"的服务未运行时,它将启动。 |
oom_score_adjust <value> | 将子进程的/proc/self/oom_score_adj设置为指定的值,该值的范围必须为-1000到1000 |
memcg.swappiness <value> | 将子进程的memory.swappiness设置为指定值(仅在安装了memcg时),该值必须等于或大于0 |
writepid <file> [ <file>* ] | fork时将子进程的pid写入给定文件。 |
seclabel <seclabel> | 在执行此服务之前,更改为" seclabel"。主要供rootfs运行的服务使用,例如 ueventd,adbd。系统分区上的服务可以根据其文件安全性上下文使用策略定义的转换。如果未指定并且策略中未定义任何过渡,则默认为init上下文。 |
capabilities <capability> [ <capability>* ] | 执行此服务时设置功能。“capabilities"应该是不带前缀” CAP_“的Linux功能,例如” NET_ADMIN"或" SETPCAP"。请参看http://man7.org/linux/man-pages/man7/capabilities.7.html上列出的Linux功能列表。 |
综上分析发现,init.rc的语法可以用一个统一的形式来理解,如下:
on <SOMETHING-HAPPEND>
<WHAT-TO-DO>
对于Actions来说,它是当<trigger>发生时去执行命令;而对于Services来说,它是always发生的(不需要启动触发条件);然后去启动指定的可执行文件(并且由Option来限制执行条件)。
5、init.rc实例分析
下面是对高通8155-LA1.1基线代码的init.rc文件部分内容分析,通过分析进一步理解它的语法规则。源码路径:system\core\rootdir\init.rc
on init #init事件
sysclktz 0 #设置基准时间为0
# Mix device-specific information into the entropy pool
# 将设备特定信息混合到熵池中s
copy /proc/cmdline /dev/urandom #将 cmdline 复制 到 urandom 中
copy /system/etc/prop.default /dev/urandom #将 prop.default 复制 到 urandom 中
symlink /proc/self/fd/0 /dev/stdin #创建一个 /proc/self/fd/0 路径的链接,目标为 /dev/stdin , 即 标准输入
symlink /proc/self/fd/1 /dev/stdout #创建一个 /proc/self/fd/1 路径的链接,目标为 /dev/stdout ,即 标准输出
symlink /proc/self/fd/2 /dev/stderr #创建一个 /proc/self/fd/2 路径的链接,目标为 /dev/stderr , 即 标准错误
symlink /system/bin /bin #创建一个 /system/bin 路径的链接,目标为 /bin
symlink /system/etc /etc #创建一个 /system/etc 路径的链接,目标为 /etc
# Backward compatibility.
# 向后兼容性。
symlink /sys/kernel/debug /d #创建一个 /sys/kernel/debug 路径的链接,目标为 /d
# Link /vendor to /system/vendor for devices without a vendor partition.
# 对于没有供应商分区的设备,将 /vendor 链接到 /system/vendor。
symlink /system/vendor /vendor #创建一个 /system/vendor 路径的链接,目标为 /vendor
# Create energy-aware scheduler tuning nodes
# 创建能量感知调度器调优节点
mkdir /dev/stune/foreground #创建目录
mkdir /dev/stune/background #创建目录
mkdir /dev/stune/top-app #创建目录
mkdir /dev/stune/rt #创建目录
chown system system /dev/stune #更改文件所有者为 system 和 组群为 system
chown system system /dev/stune/foreground #更改文件所有者为 system 和 组群为 system
chown system system /dev/stune/background #更改文件所有者为 system 和 组群为 system
chown system system /dev/stune/top-app #更改文件所有者为 system 和 组群为 system
chown system system /dev/stune/rt #更改文件所有者为 system 和 组群为 system
chown system system /dev/stune/tasks #更改文件所有者为 system 和 组群为 system
chown system system /dev/stune/foreground/tasks #更改文件所有者为 system 和 组群为 system
chown system system /dev/stune/background/tasks #更改文件所有者为 system 和 组群为 system
chown system system /dev/stune/top-app/tasks #更改文件所有者为 system 和 组群为 system
chown system system /dev/stune/rt/tasks #更改文件所有者为 system 和 组群为 system
chmod 0664 /dev/stune/tasks #更改文件访问权限为 0664
chmod 0664 /dev/stune/foreground/tasks #更改文件访问权限为 0664
chmod 0664 /dev/stune/background/tasks #更改文件访问权限为 0664
chmod 0664 /dev/stune/top-app/tasks #更改文件访问权限为 0664
chmod 0664 /dev/stune/rt/tasks #更改文件访问权限为 0664
# Create blkio group and apply initial settings.
# This feature needs kernel to support it, and the
# device's init.rc must actually set the correct values.
#创建 blkio 组并应用初始设置。
#此功能需要内核支持,设备的 init.rc 必须实际设置正确的值。
mkdir /dev/blkio/background #创建目录
chown system system /dev/blkio #更改文件所有者为 system 和 组群为 system
chown system system /dev/blkio/background #更改文件所有者为 system 和 组群为 system
chown system system /dev/blkio/tasks #更改文件所有者为 system 和 组群为 system
chown system system /dev/blkio/background/tasks #更改文件所有者为 system 和 组群为 system
chmod 0664 /dev/blkio/tasks #更改文件访问权限为 0664
chmod 0664 /dev/blkio/background/tasks #更改文件访问权限为