第24章 init进程与rc文件:系统启动的“总导演”

各位同学,今天我们来聊聊Android系统启动的“第一把火”——init进程。说实话,我当年刚接触Android Framework时,觉得init就是个简单的启动程序,直到有一次我在调试一个开机黑屏的问题时,花了整整三天才发现是init.rc里一个服务的启动顺序写错了。从那以后,我再也不敢小看这个“总导演”了。

24.1 init进程的启动流程

init进程是Linux内核启动后创建的第一个用户空间进程,PID固定为1。它的任务说白了就四个字:拉起一切

内核启动完成后,会调用/init可执行文件。这个文件位于根文件系统,是Android系统启动的起点。init进程的入口函数是main(),位于system/core/init/init.cpp

核心流程:

  1. 挂载必要的文件系统(如tmpfs、devpts、proc、sysfs)
  2. 创建/dev下的设备节点
  3. 初始化日志系统(logd)
  4. 解析init.rc脚本
  5. 启动属性服务(property_service)
  6. 初始化SELinux
  7. 进入事件循环,处理子进程信号和属性变化

我个人习惯把init进程比作一个“管家”——它先把房子打扫干净(挂载文件系统),然后拿出家规(init.rc),再安排各个仆人(服务)干活,最后坐在门口等着处理各种突发事件。

24.2 init.rc脚本解析

init.rc是init进程的“剧本”,它告诉init进程什么时候该做什么事。这个文件采用Android特有的Action语言,语法简洁但功能强大。

先看一个典型的init.rc片段:

on early-init
    # 设置init进程的优先级
    write /proc/1/oom_adj -1000

on init
    # 创建必要的目录
    mkdir /dev/socket 0775 root root
    mkdir /dev/block 0750 root root

    # 设置系统属性
    setprop ro.build.date.utc 1234567890

on boot
    # 启动核心服务
    class_start core
    class_start main

service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart zygote
    onrestart restart media

这里有几个关键概念:

概念 说明
Action(动作) 由触发条件和命令列表组成,如on boot表示系统启动时执行
Service(服务) 定义需要启动的可执行程序及其参数、权限
Command(命令) 具体的操作,如mkdirchmodsetprop
Trigger(触发器) 触发条件,如early-initinitbootproperty:xxx

我曾经遇到过一个坑:某个厂商在init.rc里把class_start core写成了class_start main,结果servicemanager没启动,整个系统卡在开机动画。嗯,这种错误排查起来真的很痛苦。

24.3 服务启动机制

init进程通过service关键字定义的服务,会在合适的时机被启动。服务的启动方式有两种:

  • 按类启动:通过class_start <class_name>启动同一类的所有服务
  • 按名启动:通过start <service_name>启动单个服务

服务启动后,init进程会监控它的状态。如果服务异常退出,init会根据配置决定是否重启:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart restart media
    onrestart restart netd

注意这里的onrestart关键字——当zygote重启时,会同时重启media和netd服务。为什么?因为zygote是Java世界的“根”,它挂了意味着所有Java服务都得重来。我在项目里就见过因为zygote崩溃导致media服务也挂掉的情况,幸好有onrestart机制自动恢复。

避坑指南:我曾经在定制系统时,把一个服务的critical属性去掉了,结果这个服务反复崩溃重启,系统却没有任何提示。后来才发现,critical属性表示“如果这个服务挂了4次以上,系统就重启进入recovery模式”。对于核心服务,一定要加上critical

24.4 属性系统(property_service)

属性系统是Android特有的“全局变量”机制。它运行在init进程中,通过socket提供读写接口。你可以把它想象成一个键值对数据库,所有进程都能访问。

属性系统的核心代码在system/core/init/property_service.cpp。它的工作流程是:

  1. init进程启动时,创建一个socket(/dev/socket/property_service
  2. 其他进程通过这个socket发送读写请求
  3. 属性服务处理请求,更新内存中的属性表
  4. 如果属性以ctl.开头,则触发控制命令(如启动/停止服务)

常用的属性操作命令:

# 读取属性
adb shell getprop ro.build.version.sdk

# 设置属性(需要root权限)
adb shell setprop debug.sf.nobootanimation 1

# 监听属性变化
adb shell getprop -p | grep ro.build

你想想看,为什么属性系统要放在init进程里?因为init是第一个进程,它拥有最完整的权限。如果放在其他进程,万一那个进程挂了,属性系统就瘫痪了。

注意:属性值有长度限制——键最长32字节,值最长92字节。我在项目中曾试图存储一个长字符串,结果被截断了,排查了半天才发现是属性系统的限制。

24.5 SELinux初始化

SELinux(Security-Enhanced Linux)是Android安全模型的基石。它采用强制访问控制(MAC),即使进程有root权限,也不能为所欲为。

init进程对SELinux的初始化流程:

  1. 读取SELinux策略文件(/sepolicy/system/etc/selinux/
  2. 调用selinux_android_load_policy()加载策略
  3. 设置安全上下文(security context)
  4. 将init进程自身设置为kernel
  5. 后续启动的所有服务都会根据策略分配安全上下文

SELinux的策略文件是编译好的二进制文件,由sepolicy工具生成。每个服务在init.rc中可以通过seclabel指定安全上下文:

service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc
    seclabel u:r:surfaceflinger:s0

如果SELinux策略配置错误,服务启动时会被拒绝访问。我记得有一次,一个第三方应用无法访问GPS,我查了三天日志,最后发现是SELinux策略里没给这个应用分配gps权限。用avc日志排查真的很考验耐心。

24.6 知识体系总览

下面这张图展示了init进程与rc文件的整体架构:

init进程与rc文件知识体系 Linux内核 init进程 (PID=1) init.rc解析 Action语言 触发器 + 命令 服务启动 class_start / start onrestart 监控 属性系统 property_service getprop / setprop SELinux初始化 策略加载 安全上下文分配

从这张图可以看出,init进程是Android系统的“总导演”,它通过解析init.rc脚本来决定启动哪些服务,通过属性系统提供全局配置,通过SELinux确保安全。这四个部分环环相扣,缺一不可。

24.7 实战经验总结

最后,分享几个我在项目中踩过的坑:

  • init.rc语法错误:少写一个空格或换行符,init进程会直接报错退出。建议用init.rc的语法检查工具验证。
  • 服务启动顺序:依赖关系一定要理清。比如servicemanager必须在其他服务之前启动,否则Binder通信会失败。
  • 属性系统性能:频繁设置属性会导致性能问题,因为每次设置都会触发socket通信和权限检查。
  • SELinux策略:在开发阶段可以临时设置为permissive模式(setenforce 0),但正式发布必须强制enforcing模式。

好了,关于init进程和rc文件的内容就讲到这里。这部分知识是Android Framework的基石,理解了它,你就能明白系统是怎么“活”起来的。


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