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事件时,大括号内的命令会依次执行。命令之间是顺序执行的,但不会等待服务启动完成——服务启动是异步的。

我曾经踩过的坑:在on boot块里写了start zygote,但zygote依赖的某些库还没挂载。结果zygote启动失败,系统卡在开机动画。后来我加了一个wait命令,确保文件系统就绪后再启动。

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启动流程。说实话,每次给新人讲这个,我都先让他们看这张图,再看代码。

Android init 进程启动流程 Linux 内核启动 init 进程启动 (PID=1) 解析 init.rc 及导入的 .rc 文件 执行 Action 触发器 启动 Service 服务 处理设备节点/属性 servicemanager / hwservicemanager zygote / surfaceflinger logd / ueventd

从这张图你能看到,init进程启动后,先解析所有rc文件,然后根据触发条件执行动作块,同时启动声明好的服务。这三个分支是并行的,但动作块内的命令是顺序执行的。

9.5 实战:如何调试init.rc

调试init.rc其实挺痛苦的,因为它在系统启动早期运行,没有完整的logcat。我分享几个我常用的方法:

  1. 加log输出:在init.rc里用log命令打印信息。比如log "I'm here",这些日志会输出到kernel log中,可以用dmesg查看。
  2. 模拟启动顺序:在开发板上,我经常用setprop手动触发某个事件,然后观察服务是否按预期启动。
  3. 检查服务状态:系统起来后,用getprop | grep init.svc查看所有服务的状态。running、stopped、restarting一目了然。
核心要点:init.rc的解析是一次性的,系统启动后修改rc文件不会生效。如果你想动态调整,得用setprop配合on property:触发器来实现。

好了,关于init.rc和启动流程,今天就讲到这里。记住,init是Android系统的基石,理解它的工作机制,对后续学习zygote、SystemServer乃至整个Android框架都大有裨益。


公众号:蓝海资料掘金营,微信deep3321