6. 通信模型:Client-Server-ServiceManager 三角关系详解

Binder 通信里最核心的,就是这三个角色:Client、Server 和 ServiceManager。很多人刚接触时觉得绕,其实说白了,这就是一个「中介 + 供需双方」的模型。我当年第一次看源码时也被这三角关系搞得头大,后来画了张图才彻底理清。

一句话总结:Server 向 ServiceManager 注册,Client 向 ServiceManager 查询,然后 Client 和 Server 直接通信。

6.1 三个角色各司其职

我们先看看每个角色具体干什么:

  • Client(客户端):想要调用某个服务的进程。比如一个 App 想获取系统电量信息,它就是 Client。
  • Server(服务端):提供具体功能的进程。比如 BatteryService 就是 Server,它知道电池的实时状态。
  • ServiceManager(服务大管家):一个特殊的 Binder 节点,负责管理所有注册上来的服务。它的句柄永远是 0。

嗯,这里要注意:ServiceManager 本身也是一个 Binder 服务,但它比较特殊——系统启动时第一个被初始化,而且它的引用(句柄 0)是写死在 Binder 驱动里的。我刚开始调试时,总以为 ServiceManager 是内核的一部分,其实它是个用户态进程,叫 servicemanager

6.2 三角关系的完整流程

整个通信过程分三步走:

  1. Server 注册:Server 进程启动后,通过 Binder 驱动把自己的服务名和 Binder 引用告诉 ServiceManager。
  2. Client 查询:Client 进程通过句柄 0 找到 ServiceManager,问它「我要的服务在哪?」。
  3. 直接通信:ServiceManager 把 Server 的 Binder 引用返回给 Client,之后 Client 和 Server 直接通过 Binder 驱动交互,不再经过 ServiceManager。

为什么第三步要绕开 ServiceManager?你想啊,如果每次调用都经过它,那它就成了性能瓶颈。Binder 的设计哲学就是「查询一次,永久直连」。我在优化系统服务时,曾经误以为 ServiceManager 会做请求转发,结果发现它只负责「牵线搭桥」,真正的数据交换是 Client 和 Server 之间点对点完成的。

6.3 核心流程图

下面这张 SVG 图把三角关系画得很清楚,我建议你盯着看 30 秒,比读一千字都管用。

Binder 三角关系通信模型 Client (客户端) Server (服务端) ServiceManager (服务大管家) ① 注册服务 ② 查询服务 返回 Binder 引用 ③ 直接通信(不经过 ServiceManager) 句柄 0 始终指向 ServiceManager,Client 通过它完成服务发现 注册和查询只发生一次,后续通信是点对点的 注册 查询 直接通信

6.4 关键数据结构

ServiceManager 内部维护了一张服务注册表。我翻过 Android 9 的源码,它的核心就是一个 svclist 链表。每个节点大概长这样:

struct svcinfo {
    struct svcinfo *next;   // 链表指针
    uint32_t handle;        // Binder 句柄
    struct binder_death *death;  // 死亡通知
    int allow_isolated;     // 是否允许隔离进程访问
    size_t len;             // 服务名长度
    uint16_t name[0];       // 服务名(柔性数组)
};

每次 Server 调用 addService(),ServiceManager 就创建一个 svcinfo 节点挂到链表上。Client 调用 getService() 时,就遍历这个链表匹配服务名。说白了就是个简单的名字查找。

避坑指南:我曾经在调试一个系统服务时,发现 Client 总是拿不到 Server 的引用。查了半天,原来是 Server 注册时服务名写错了大小写。ServiceManager 的匹配是严格区分大小写的,"battery""Battery" 是两个不同的服务。

6.5 为什么需要 ServiceManager?

有人可能会问:为什么不直接把 Server 的 Binder 引用硬编码到 Client 里?

原因很简单:解耦。Server 进程可能崩溃重启,每次重启后 Binder 驱动会分配新的句柄。如果 Client 里写死了句柄,Server 一重启就全崩了。有了 ServiceManager,Server 重启后重新注册,Client 重新查询一次就行。

我记得有一次线上问题,某个系统服务频繁重启,但上层 App 完全无感知——就是因为 ServiceManager 的死亡通知机制让 Client 自动重新查询了服务。嗯,这就是中间层的价值。

6.6 注册与查询的底层细节

从 Binder 驱动角度看,注册和查询其实都是 BC_TRANSACTIONBC_REPLY 的变种。ServiceManager 的 Binder 节点比较特殊,它处理的是 BINDER_SET_CONTEXT_MGR 命令。

具体流程:

  • 注册:Server 发送 BC_TRANSACTION,code 为 ADD_SERVICE_TRANSACTION,数据里包含服务名和 Binder 引用。ServiceManager 收到后,把引用保存到 svclist 中。
  • 查询:Client 发送 BC_TRANSACTION,code 为 CHECK_SERVICE_TRANSACTION,数据里只包含服务名。ServiceManager 查表后,把对应的 Binder 引用返回给 Client。

注意:ServiceManager 本身不参与数据通信,它只做「名字到引用」的映射。一旦 Client 拿到了 Server 的 Binder 引用,后续所有方法调用都是 Client 和 Server 之间直接通过 Binder 驱动完成的。ServiceManager 不会也不应该看到你的业务数据。

6.7 一个典型的调用链

假设 App 想获取电池电量:

  1. App(Client)通过句柄 0 向 ServiceManager 发送查询请求,服务名为 "battery"
  2. ServiceManager 在 svclist 中找到 BatteryService 的 Binder 引用,返回给 App。
  3. App 拿到引用后,直接向 BatteryService 发送 getProperty() 请求。
  4. BatteryService 处理请求,返回电量值。
  5. 整个过程,ServiceManager 只在第 1、2 步出现一次。

这个模型的好处是:ServiceManager 的负载极低,不会成为瓶颈。我做过压力测试,即使每秒上千次服务查询,ServiceManager 的 CPU 占用也不到 1%。

6.8 总结

Client-Server-ServiceManager 这个三角关系,是理解 Binder 通信的基石。记住三点:

  • ServiceManager 是中介,只负责注册和查询。
  • 真正的通信是 Client 和 Server 直连。
  • 句柄 0 是通往 ServiceManager 的唯一入口。

搞懂了这些,后面再看 Binder 驱动的数据拷贝、线程池管理这些细节,就会轻松很多。


专注资料整理