一、从 ServiceManager 到 servicemanager.rc:一段进化的故事

聊到 Binder 机制,ServiceManager 是个绕不开的角色。我刚开始接触 Android 时,总觉得它就是个「电话总机」——谁要服务,找它查号就行。但后来深入源码才发现,这个「总机」本身也在不断进化。

今天我们就聊聊 ServiceManager 的演变史。说白了,就是从一段 C 代码,变成了一个 .rc 文件里的服务声明。这背后藏着 Android 系统架构的深刻变化。

1.1 早期的 ServiceManager:一个「裸奔」的守护进程

在 Android 早期版本(4.4 之前),ServiceManager 是怎么启动的?

嗯,它其实是在 init.rc 里被直接启动的。你翻翻旧代码,会看到类似这样的片段:

# 早期 init.rc 中的 ServiceManager 启动
service servicemanager /system/bin/servicemanager
    class core
    user system
    group system
    critical
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger

这段配置看起来挺简单,对吧?但这里有个关键点:servicemanager 是作为核心服务启动的。它挂了,整个系统都得重启。

我记得有一次在项目中调试一个诡异的问题——系统启动到一半就卡住了。查了半天,发现是 servicemanager 进程被 kill 了,然后 zygote、media、surfaceflinger 全部跟着重启,形成了死循环。嗯,那时候我才真正理解「critical」这个标记的分量。

1.2 为什么需要进化?

早期的方案有什么问题?我总结了几点:

  • 硬编码依赖:ServiceManager 的启动参数、权限、依赖关系都写死在 init.rc 里
  • 缺乏灵活性:想改个参数?得改 init.rc,还得重新编译整个系统
  • 安全隔离不足:ServiceManager 运行在 system 用户下,权限太大
  • 调试困难:出了问题,你只能看 log,没有细粒度的控制手段

你想想看,一个系统里最核心的服务,居然用这么原始的方式管理,是不是有点说不过去?

1.3 servicemanager.rc 的诞生

从 Android 5.0(Lollipop)开始,Google 引入了新的服务管理机制。ServiceManager 的启动配置被独立出来,变成了 servicemanager.rc 文件。

这个文件长什么样?我贴一段典型的代码:

# servicemanager.rc - Android 5.0+ 版本
service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    critical
    onrestart restart zygote
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart drm
    onrestart restart hal
    writepid /dev/cpuset/foreground/tasks

看到变化了吗?多了几个关键点:

  • class core animation:加入了 animation 类,意味着它和动画服务有依赖关系
  • group system readproc:增加了 readproc 权限组
  • writepid:写 cgroup 控制组,做资源隔离
  • 更多的 onrestart 依赖:drm、hal 等服务也被纳入重启链

我个人习惯把这种变化叫做「从硬编码到声明式」的转变。说白了,就是把服务的配置信息从代码里抽出来,放到一个独立的配置文件中。这样做的最大好处是——你可以不改代码,只改配置文件,就能调整服务的行为

1.4 进化的核心:SELinux 策略的引入

这个进化过程中,最值得关注的是 SELinux 的引入。从 Android 5.0 开始,ServiceManager 的运行受到了 SELinux 策略的严格约束。

我们来看一个 SELinux 策略文件的片段:

# servicemanager.te - SELinux 策略
type servicemanager, domain;
type servicemanager_exec, exec_type, file_type;

init_daemon_domain(servicemanager)

# 允许 servicemanager 访问 binder 设备
allow servicemanager self:binder { transfer call };
allow servicemanager servicemanager:binder { transfer call };

# 允许 servicemanager 读取系统属性
allow servicemanager property_type:file read;
allow servicemanager system_prop:property_service set;

这段策略文件定义了:

  • servicemanager 运行在独立的 domain 中
  • 它只能访问特定的 binder 设备
  • 它只能读取特定的系统属性
  • 它不能随意访问文件系统

我曾经在项目中遇到过一个问题:某个第三方应用想注册一个系统服务,结果一直失败。查了半天,发现是 SELinux 策略限制了 servicemanager 的 binder 调用权限。嗯,从那以后,我每次修改服务注册逻辑,都会先检查 SELinux 策略。

1.5 架构对比:两张图看懂变化

为了让你更直观地理解这个进化,我画了两张架构对比图。

早期架构(Android 4.4 之前) init.rc(硬编码配置) servicemanager 进程 Binder 驱动(无 SELinux 约束) 特点:配置硬编码、无安全隔离、调试困难 现代架构(Android 5.0+) servicemanager.rc servicemanager 进程 SELinux 策略(servicemanager.te) Binder 驱动(SELinux 约束) 特点:配置独立、安全隔离、灵活调试

这张图清晰地展示了两个时代的差异。左边是「大锅饭」时代,所有配置混在一起;右边是「精细化管理」时代,每个服务都有自己的配置文件和安全策略。

1.6 关键变化总结

我把这个进化的关键变化整理成了一个表格:

维度 早期(Android 4.4-) 现代(Android 5.0+)
配置文件 init.rc 硬编码 独立的 servicemanager.rc
安全策略 无 SELinux 约束 严格的 SELinux 策略
权限控制 system 用户,权限过大 精细化权限组(readproc 等)
资源隔离 无 cgroup 控制 writepid 到 cgroup
依赖管理 简单的 onrestart 链 更完整的依赖关系(drm、hal 等)
调试手段 仅 logcat selinux 审计日志 + 细粒度控制

1.7 避坑指南:我踩过的几个坑

聊了这么多理论,分享几个实战中遇到的坑吧:

⚠️ 坑一:修改 servicemanager.rc 后不生效

我曾经修改了 servicemanager.rc 里的 group 配置,结果重启后完全不生效。查了半天才发现,原来是 SELinux 策略限制了 group 的变更。记住:修改 .rc 文件后,必须同步更新对应的 SELinux 策略文件

⚠️ 坑二:onrestart 链导致的启动风暴

有一次我在调试一个系统服务,不小心把 servicemanager 的 onrestart 链写成了循环依赖。结果系统启动时,servicemanager 挂了 → zygote 重启 → media 重启 → surfaceflinger 重启 → 然后又导致 servicemanager 挂掉... 嗯,那场面,简直是灾难。建议:画清楚依赖图,避免循环依赖

💡 小技巧:如何快速验证 SELinux 策略

我习惯用 adb shell dmesg | grep avc 查看 SELinux 审计日志。如果 servicemanager 启动失败,先看这个日志,90% 的问题都能定位到。

1.8 核心要点回顾

好了,这一章的内容就到这里。我帮你梳理一下核心要点:

  • ServiceManager 的启动配置从 init.rc 独立为 servicemanager.rc,实现了配置与代码的分离
  • SELinux 策略的引入,让 ServiceManager 的运行有了严格的安全边界
  • 资源隔离和依赖管理更加精细化,cgroup、writepid 等机制被引入
  • 调试手段更加丰富,SELinux 审计日志成为定位问题的利器

这个进化过程,说白了就是 Android 系统从「能用」到「安全、可控、易维护」的缩影。你想想看,一个简单的服务启动配置,背后竟然牵扯到安全策略、资源管理、依赖关系这么多东西。这就是系统工程师的日常——每一个细节都可能藏着坑。

下一章,我们会深入 ServiceManager 的源码,看看它到底是怎么注册和查询服务的。到时候你会发现,代码里的细节比配置文件的进化还要精彩。


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