Android init.rc 超详细解析
init.rc 是 Android 系统启动流程中,由 init 进程解析和执行的核心配置文件,定义了系统服务、启动脚本、权限控制、挂载规则等关键行为,是理解 Android 启动机制的核心。下面从语法规则、核心组件、执行流程、实战案例、常见问题五个维度拆解。
一、基础定位:它在启动流程里的角色
结合你之前看的启动流程图,init 进程是内核启动后的第一个用户态进程,它的核心工作就是:
- 解析
init.rc及所有引入的.rc配置文件 - 按照配置创建 / 挂载文件系统、设置权限
- 启动关键进程(如
service_manager、Zygote、surfaceflinger) - 管理系统服务的生命周期(重启、崩溃处理)
init.rc 本质是一种Android Init Language(AIL) 编写的脚本,不是通用 Shell 脚本,语法有严格规范。
二、核心语法规则(必懂基础)
init.rc 的语法由四大核心元素 构成:Actions、Services、Commands、Triggers,同时支持 Options 控制行为。
1. 基本格式
- 注释:以
#开头,到行尾结束 - 关键字:区分大小写,必须小写
- 缩进:用空格 / 制表符,同层级语句缩进一致(影响解析)
- 跨多行:用
\换行
2. 四大核心元素详解
(1)Triggers(触发器)
定义触发条件,当条件满足时,会执行绑定的 Action。常见触发器:
表格
| 触发器 | 触发时机 |
|---|---|
early-init |
init 进程刚启动,挂载基础文件系统前 |
init |
挂载基础文件系统后,关键服务启动前 |
late-init |
核心系统服务启动完成后 |
boot |
系统启动完成,进入用户态前 |
property:ro.bootmode=charger |
当系统属性 ro.bootmode 变为 charger 时触发 |
sys.powerctl=* |
电源控制事件触发(关机、重启) |
(2)Actions(动作块)
格式:on <trigger> [&& <trigger>]*,定义触发器满足时要执行的 Commands 序列。示例:
rc
# 当 boot 触发器触发时,执行这些命令
on boot
# 挂载 /data 分区(示例)
mount_all /vendor/etc/fstab.${ro.boot.hardware.platform}
# 设置系统属性
setprop ro.boottime.init.boot_complete 1
# 创建目录并设置权限
mkdir /data/misc 0771 system system
(3)Services(服务块)
格式:service <name> <path> [arguments]*,定义由 init 进程管理的系统服务(常驻进程)。核心属性:
name:服务名称,唯一标识path:服务可执行文件路径arguments:启动参数- 后续可跟
Options控制服务行为
示例:
rc
# 定义 servicemanager 服务
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
socket servicemanager stream 0660 root system
(4)Commands(命令)
Action 块中可执行的内置命令,是 init 进程直接实现的逻辑,不是 Shell 命令。常见命令:
表格
| 命令 | 作用 | 示例 |
|---|---|---|
exec |
执行外部程序(阻塞式) | exec /system/bin/mount_ext4 /dev/block/by-name/userdata /data |
mkdir |
创建目录 | mkdir /data/system 0700 system system |
mount |
挂载文件系统 | mount ext4 /dev/block/sda1 /system ro |
setprop |
设置系统属性 | setprop ro.build.version.sdk 33 |
chmod/chown |
修改文件权限 / 所有者 | chmod 0644 /sys/class/leds/lcd-backlight/brightness |
write |
向文件写入内容 | write /sys/power/state mem |
symlink |
创建软链接 | symlink /system/bin/toolbox /system/bin/ls |
注意:
init.rc中不能直接写 Shell 命令 (如ls、echo),必须用内置命令或exec调用外部可执行文件。
3. 服务块的关键 Options(控制服务行为)
这些选项直接影响服务的启动方式、权限和生命周期,是系统服务定义的核心:
表格
| Option | 作用 | 示例场景 |
|---|---|---|
class <name> |
指定服务所属类别,可通过 start <class> 批量启动 |
class core(核心服务,boot 阶段必须启动)、class main(普通服务) |
user <username> |
服务运行的用户(默认 root) | user system(以 system 用户运行,避免 root 权限过大) |
group <groupname> [groupname]* |
服务运行的用户组 | group system radio(同时加入 system 和 radio 组,获取权限) |
critical |
标记为关键服务,崩溃 4 次则系统重启 | servicemanager、zygote 等核心服务必须标记 |
oneshot |
服务退出后不自动重启(一次性任务) | 分区格式化、初始化脚本 |
disabled |
不随类别自动启动,需手动 start <name> |
调试用服务、可选组件 |
socket <name> <type> <perm> [user] [group] [seclabel] |
创建 Binder/UNIX 域套接字,供进程间通信 | socket servicemanager stream 0660 root system |
priority <priority> |
设置进程优先级(-20 到 19,默认 0) | priority -10(提高关键服务优先级) |
seclabel <label> |
设置 SELinux 安全上下文 | seclabel u:r:init:s0 |
三、init.rc 的执行流程(从解析到运行)
结合你之前的启动流程图,init.rc 的完整执行路径如下:
1. 解析阶段
init进程启动后,首先解析/init.rc(根目录的主配置文件)- 递归解析所有引入的配置文件:
- 主
init.rc会引入/system/etc/init/、/vendor/etc/init/、/odm/etc/init/下的所有.rc文件 - 厂商定制的服务、硬件相关配置,通常放在
/vendor/etc/init/下(如vendor.rc)
- 主
- 解析完成后,将所有
Action按触发器分组,Service存入服务列表
2. 执行阶段(按触发器顺序)
early-init阶段 :挂载tmpfs、devpts等基础文件系统,初始化 SELinux,设置基础权限init阶段 :挂载/system、/vendor等分区,启动core类服务(servicemanager、hwservicemanager)late-init阶段 :启动main类服务(surfaceflinger、media_server等),初始化硬件服务boot阶段 :所有核心服务启动完成,触发boot触发器,执行桌面启动前的初始化- 后续阶段:响应系统属性变化、电源事件等触发器,动态执行对应
Action
关键细节:
init进程是单线程事件循环 ,Action中的命令按顺序执行,前一个命令完成后才会执行下一个,阻塞式命令(如exec)会卡住后续流程。
四、实战解析:典型系统服务的 init.rc 配置
以你流程图中的 servicemanager 和 Zygote 为例,拆解真实配置的含义:
1. servicemanager 配置(核心 Binder 服务管理器)
rc
service servicemanager /system/bin/servicemanager
class core
user system
group system
critical
socket servicemanager stream 0660 root system
class core:属于核心服务,init会在init阶段强制启动,崩溃后自动重启critical:崩溃 4 次会触发系统重启,是 Android 的 "心脏服务"socket:创建一个 UNIX 域套接字,供其他进程注册 / 查询 Binder 服务(如surfaceflinger、media_server)
2. Zygote 配置(Java 进程孵化器)
rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
app_process64:Zygote 的可执行文件,负责孵化所有 Java 进程(包括system_server和普通应用)--start-system-server:启动时同时创建system_server进程(你流程图中的关键节点)onrestart:当 Zygote 重启时,触发这些命令(唤醒系统、重启依赖服务)
五、厂商定制与常见修改场景
1. 厂商配置的存放位置
Android 分区化设计中,不同层级的配置文件存放位置不同:
表格
| 分区 | 路径 | 用途 |
|---|---|---|
/system |
/system/etc/init/ |
原生 Android 系统服务配置 |
/vendor |
/vendor/etc/init/ |
芯片厂商 / 设备厂商定制的硬件服务配置 |
/odm |
/odm/etc/init/ |
设备厂商的机型定制配置 |
/product |
/product/etc/init/ |
系统应用 / 服务的定制配置 |
2. 常见修改场景(开发 / 调试)
场景 1:添加自定义后台服务
rc
# 定义一个自定义服务,开机启动,崩溃自动重启
service my_custom_service /vendor/bin/my_service
class main
user system
group system
disabled # 先禁用,调试时再启用
oneshot # 一次性服务,退出不重启(可选)
# 在 boot 阶段启动该服务
on boot
start my_custom_service
场景 2:修改分区挂载参数
rc
# 调整 /data 分区的挂载权限,开启 discard(TRIM)优化
on init
mount_all --early /vendor/etc/fstab.${ro.boot.hardware.platform}
# 重新挂载 /data,添加 discard 选项
mount ext4 /dev/block/by-name/userdata /data ro,discard
场景 3:添加调试命令
rc
# 在 boot 阶段执行调试脚本,输出启动日志
on boot
exec /system/bin/log -t init_debug "Boot completed, running custom debug script"
exec /vendor/bin/debug_init.sh
六、常见问题与避坑指南
1. 为什么 init.rc 里的命令不执行?
- 检查触发器:确认命令所在的
Action触发器是否触发(如boot阶段的命令,必须等所有核心服务启动后才会执行) - 权限问题:
init进程运行在 root 权限,但 SELinux 会限制操作,可通过dmesg | grep avc查看 SELinux 拒绝日志 - 缩进错误:
init.rc解析器对缩进敏感,同层级语句缩进不一致会导致解析失败 - 命令错误:不能直接写 Shell 命令,必须用内置命令或
exec调用外部可执行文件
2. 服务启动失败,日志在哪里看?
init进程日志:adb logcat -s init- 服务崩溃日志:
adb logcat -s ServiceManager或adb shell dmesg - 关键错误日志:
/proc/kmsg(内核日志,包含init阶段的错误)
3. oneshot 和 disabled 选项的区别
oneshot:服务退出后不自动重启,适合一次性任务(如初始化脚本)disabled:服务默认不启动,必须通过start <service_name>手动触发,适合调试 / 可选服务
七、进阶:Android 10+ 的 init 架构变化
从 Android 10 开始,init 引入了分区化配置 和服务管理优化:
- 废弃了
/init.rc中的大量硬编码逻辑,改为模块化的.rc文件,每个服务对应独立的配置文件(如servicemanager.rc) - 引入
init阶段的依赖管理,支持服务间依赖(如servicemanager启动后,再启动surfaceflinger) - 强化 SELinux 安全控制,所有服务的
seclabel必须在配置中明确指定,禁止隐式权限