在这篇博客中,深入探讨了Android系统中的init.rc文件,这是Android系统启动和运行的关键部分。详细解析了init.rc文件中的命令和动作,包括内置命令,外部命令,属性触发器和事件触发器,并且详细介绍了它们的使用和定义。还讨论了Android 11++中init.rc文件的执行顺序,以及如何根据系统属性和事件来定义和触发动作。这篇博客对于理解和控制Android系统的启动和运行过程非常有帮助。
系列文章
Android系统 init.rc 第一次开机创建文件节点实现和原理分析
Android系统 init.rc sys/class系统节点写不进解决方案和原理分析
Android系统 自定义动态修改init.custom.rc
1. init.rc文件概述
1.1 init.rc文件的定义与作用
- init.rc文件是一个配置文件,由Android初始化语言编写的脚本,主要包含五种类型语句:Action、Command、Service、Option、Import。
- init.rc文件由Android系统第一个启动的init程序解析,此文件由语句组成,主要包含了四种类型的语句:Action, Commands,Services, Options.
- init.rc文件是Android系统启动时执行的脚本文件,它负责系统的初始设置,如挂载文件系统,创建目录,设置属性,启动服务等。
1.2 Android启动流程中init.rc的角色
- Android启动流程可以分为三个主要阶段:第一阶段初始化(first_stage_init),SELinux设置(selinux_setup),第二阶段初始化(second_stage_init)。
- 第一阶段初始化负责设置最低限度的基本需求用以加载系统其余部分,具体来说,包括“/dev”,“/proc”的挂载,挂载“early mount”分区 (这包括所有包含系统代码的分区,例如system和vendor)。对于有ramdisk的设备,将system.img挂载到“/”。对于没有ramdisk的设备,将system.img挂载到“/system_root”。
- SELinux设置阶段可选地编译并加载SELinux到系统中,这个阶段主要是加载初始化selinux相关的东西,如sepolicy、seapp_contexts等。
- 第二阶段初始化是init的主要阶段,将运行并通过init继续引导init.rc脚本。在这个阶段中,init会解析init.rc文件和其他相关的rc文件,并根据其中定义的命令和服务来完成系统启动。例如,
init会导入init.super.rc文件(我在rk3568没看到这个文件),用于挂载super分区和动态分区;导入init.hardware.rc文件,用于启动硬件相关的服务;导入init.zygote64_32.rc文件,用于启动Zygote进程和应用程序框架等。
2. init.rc文件结构和语法
2.1 init.rc文件的基本结构
- init.rc文件由多个部分组成,每个部分以一个关键字开头,并用花括号括起来。例如:
service <name> <pathname> [ <argument> ]* { <option> <option> ... }
- 关键字有以下几种:service, action, onrestart, import, writepid。
- service表示定义一个服务,action表示定义一个动作,onrestart表示定义一个服务重启时执行的命令,import表示导入其他rc文件,writepid表示将服务进程的PID写入到指定文件。
2.2 init.rc文件的语法规则
- init.rc文件中的每一行都是一个语句或者一个注释。注释以#开头,并且只能单独占一行。
- 语句的格式为:关键字 参数1 参数2 … 参数n
- 参数之间用空格或者制表符分隔,不能用逗号或者其他符号。
- 参数可以是字符串,数字,变量或者表达式。字符串必须用双引号括起来,数字可以是十进制或者十六进制(以0x开头)。变量以$开头,可以是系统属性或者环境变量。表达式可以是比较运算符(==, !=, <, >, <=, >=)或者逻辑运算符(&&, ||, !)组合的布尔值。
- 语句可以跨越多行,但是必须在每行的末尾加上反斜杠(\)表示续行。
3. init.rc文件中的命令和动作
3.1 常用命令详解
命令是init.rc文件中的基本元素,它们用于执行一些具体的操作,如挂载文件系统,设置属性,启动服务等。命令有以下几种类型:
内置命令
这些命令是init进程直接实现的,它们通常用于执行一些基本的系统操作,如挂载文件系统,设置系统属性,启动和停止服务等。这些命令是Android系统启动和运行的基础,它们在系统的整个生命周期中都可能被使用。
命令 | 示例 | 说明 | 参数 |
setprop | setprop ro.debuggable 1 | 设置一个系统属性的值 | 是属性名,是属性值 |
getprop | getprop ro.bootmode | 获取一个系统属性的值 | 是属性名 |
start | start surfaceflinger | 启动一个服务进程 | 是服务名 |
stop | stop surfaceflinger | 停止一个服务进程 | 是服务名 |
restart | restart media | 重启一个服务进程 | 是服务名 |
class_start | class_start core | 启动一类服务进程 | 是服务类别 |
class_stop | class_stop core | 停止一类服务进程 | 是服务类别 |
class_reset | class_reset core | 重置一类服务进程的状态 | 是服务类别 |
trigger | trigger init | 触发一个触发器段落的执行 | <trigger_name>是触发器名 |
write | write /sys/class/leds/lcd-backlight/brightness 255 | 向一个文件写入一个值 | 是文件路径,是写入值 |
copy | copy /system/etc/init/hw/init.{ro.hardware}.rc /data/misc/wifi/wpa_supplicant.conf | 复制一个文件到另一个文件 | <src_path>是源文件路径,<dst_path>是目标文件路径 |
symlink | symlink /system/bin/linker /system/bin/linker64 | 创建一个软链接到另一个文件 | <target_path>是目标文件路径,<link_path>是软链接路径 |
chmod | chmod 0660 /data/misc/wifi/wpa_supplicant.conf | 修改一个或多个文件的权限模式 | 是八进制数表示的权限位,<path…>是文件路径列表 |
chown | chown system system /data/misc/wifi/wpa_supplicant.conf | 改变文件或目录的所有者和组 | .是所有者和组,是文件或目录路径 |
mount | mount ext4 /dev/block/mmcblk0p1 /system | 挂载文件系统 | 文件系统类型,设备,挂载点 |
umount | umount /system | 卸载文件系统 | 挂载点 |
外部命令
这些命令是由系统提供的可执行文件实现的,它们通常用于执行一些更复杂的操作,如创建和删除目录,修改文件权限等。这些命令通常在系统启动时被执行,用于设置系统的初始状态。
命令 | 示例 | 说明 | 参数 |
mkdir | mkdir /data/misc/wifi 0770 wifi wifi | 创建目录 | 路径,权限,所有者,组 |
rmdir | rmdir /data/misc/wifi | 删除目录 | 路径 |
rm | rm /data/misc/wifi/wpa_supplicant.conf | 删除文件 | 文件路径 |
exec | exec – /system/bin/sh -c “mkdir /data/app-private” | 执行一个命令,通常用于执行一些复杂的shell命令 | 命令行 |
属性触发器
这些命令是由系统属性的变化触发的,它们通常用于响应系统状态的变化,如电池电量低,网络连接变化等。这些命令可以让我们在系统属性变化时执行一些特定的操作,以适应新的系统状态。
命令 | 示例 | 说明 | 参数 |
on property:= | on property:ro.bootmode=charger | 当属性的值变为时,执行后面的命令 | 是属性名,是属性值 |
事件触发器
这些命令是由系统事件触发的,它们通常用于响应系统的一些特定事件,如系统启动,设备插入等。这些命令可以让我们在特定的系统事件发生时执行一些特定的操作,以响应这些事件。
命令 | 示例 | 说明 | 参数 |
on | on boot | 当事件发生时,执行后面的命令 | 是事件名 |
以上是一些常见的命令,但并不是全部,Android init语言的完整语法可以在Android源代码的system/core/init/README.md文件中找到。
3.2 常用动作详解
- 动作是由一组命令组成的,它们在满足某个触发条件时执行。动作的格式为:
on <trigger> <command> <command> ...
- 触发条件可以是系统属性或者系统事件。系统属性的格式为:
property:<name>=<value>
,表示当属性<name>
的值变为<value>
时触发。系统事件的格式为:<event>
,表示当<event>
事件发生时触发。常用的系统事件有以下几种:
顺序 | 阶段 | 描述 |
1 | on early-init |
在系统启动的早期阶段,此时只有根文件系统是可写的,其他如/system 、/data 等都是只读的。 |
2 | on init |
在系统初始化阶段,此时还不能访问/system 、/data 等目录。 |
3 | on early-fs |
在文件系统准备好之后的早期阶段,此时可以访问/system 等目录,但/data 等目录可能还是只读的。 |
4 | on fs |
在文件系统准备好之后,此时可以访问/system 等目录,但/data 等目录可能还是只读的。 |
5 | on post-fs |
在文件系统挂载之后,此时可以访问大部分目录,但/data 等目录可能还是只读的。 |
6 | on post-fs-data |
在/data 等目录准备好之后,此时可以访问所有目录。 |
7 | on early-boot |
在系统启动的早期阶段,此时可以访问所有目录,并且所有服务都已经启动。 |
8 | on boot |
在系统启动完成之后,此时可以访问所有目录,并且所有服务都已经启动。 |
9 | on post-boot |
在系统启动完成后的后期阶段,此时可以访问所有目录,并且所有服务都已经启动,通常用于启动一些非关键服务。 |
10 | on property:<name>=<value> |
当系统属性<name> 的值变为<value> 时,执行后面的命令。 |
11 | on <event> |
当<event> 事件发生时,执行后面的命令。常见的事件有boot (系统启动完成)、fs (文件系统已经挂载)、post-fs-data (/data 分区挂载后执行)、charger (充电模式启动)等。 |
- 动作中的命令可以是内置命令,外部命令,或者其他动作的触发器。例如:
on boot # Start the low-level logging daemon start logd # Start the kernel logging daemon start klogd # Trigger late init trigger late-init
- 动作可以嵌套定义,即一个动作中可以包含另一个动作的定义。例如:
on property:sys.boot_completed=1 # Action to run upon boot completion. on boot_complete trigger boot_complete
4. init.rc文件编写
4.1. 编写自己的init.rc文件
算了 尝试了几种方法都加不成功 , 后面再补充…
希望对你有所帮助。如果你有任何问题或建议,欢迎留言讨论。谢谢!