9、init.rc与启动流程:init进程解析、rc文件语法、服务启动控制
大家好,我是你们的嵌入式Android开发讲师。今天我们来聊聊Android启动流程中一个绕不开的核心——init进程和它的配置文件init.rc。
说实话,我刚入行那会儿,觉得init.rc就是个启动脚本,跟Linux的rc.local差不多。后来在项目里被坑过一次,才发现这东西远比我想象的复杂。嗯,今天就把我这些年踩过的坑和总结的经验,一次性讲清楚。
9.1 init进程:Android世界的第一个进程
Android系统启动后,内核加载完毕,第一个用户空间进程就是init。它的PID永远是1。你想想看,所有其他进程都是它的子进程或后代。
init进程主要干三件事:
- 解析init.rc及其导入的配置文件
- 创建并管理关键系统服务(如servicemanager、zygote)
- 处理设备节点的创建、权限设置等
我在项目中遇到过一个问题:某款硬件平台的GPIO驱动加载时序不对,导致触摸屏无法工作。查了两天,最后发现是init.rc中服务的启动顺序没调对。从那以后,我对init.rc的解析流程格外上心。
9.2 init.rc文件语法:没那么神秘
init.rc的语法其实很简单,它由一系列语句组成。每个语句以关键字开头,后面跟参数。我习惯把它分成三类:
| 类型 | 关键字示例 | 作用 |
|---|---|---|
| 动作(Action) | on early-init, on boot | 在特定触发条件执行一组命令 |
| 服务(Service) | service servicemanager /system/bin/servicemanager | 声明一个可执行的服务进程 |
| 导入(Import) | import /init.${ro.hardware}.rc | 引入其他rc文件 |
来看一个典型的动作块:
on boot
# 设置系统属性
setprop ro.sys.foo bar
# 启动核心服务
start servicemanager
start hwservicemanager
# 创建目录
mkdir /data/misc 0770 system system
这里要注意,on boot是触发条件,当系统触发boot事件时,大括号内的命令会依次执行。命令之间是顺序执行的,但不会等待服务启动完成——服务启动是异步的。
9.3 服务启动控制:不仅仅是start/stop
服务声明是init.rc的重头戏。一个典型的服务声明长这样:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc
socket zygote stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
这里每个选项都有讲究:
- class:服务分组。同组的服务可以一起启动/停止。比如
class_start main会启动所有class为main的服务。 - onrestart:当服务重启时执行的动作。常用于级联重启——比如surfaceflinger挂了,需要同时重启gralloc和hwcomposer。
- socket:自动创建socket文件,省得服务自己写代码创建。
- writepid:将进程PID写入特定文件,用于cgroup管理。
我个人习惯在调试阶段给服务加一个disabled选项,这样init不会自动启动它。等系统起来后,我再通过adb shell手动启动,方便观察日志。
oneshot选项。但注意,oneshot服务只启动一次,不会自动重启。如果你既想自动重启,又不想无限重启,可以用restart_period设置重启间隔。
9.4 启动流程全貌:一张图说清楚
下面这张图是我自己整理的init启动流程。说实话,每次给新人讲这个,我都先让他们看这张图,再看代码。
从这张图你能看到,init进程启动后,先解析所有rc文件,然后根据触发条件执行动作块,同时启动声明好的服务。这三个分支是并行的,但动作块内的命令是顺序执行的。
9.5 实战:如何调试init.rc
调试init.rc其实挺痛苦的,因为它在系统启动早期运行,没有完整的logcat。我分享几个我常用的方法:
- 加log输出:在init.rc里用
log命令打印信息。比如log "I'm here",这些日志会输出到kernel log中,可以用dmesg查看。 - 模拟启动顺序:在开发板上,我经常用
setprop手动触发某个事件,然后观察服务是否按预期启动。 - 检查服务状态:系统起来后,用
getprop | grep init.svc查看所有服务的状态。running、stopped、restarting一目了然。
setprop配合on property:触发器来实现。
好了,关于init.rc和启动流程,今天就讲到这里。记住,init是Android系统的基石,理解它的工作机制,对后续学习zygote、SystemServer乃至整个Android框架都大有裨益。
公众号:蓝海资料掘金营,微信deep3321