Binder驱动与Service Manager的职责与交互

这一章我们来聊聊Binder体系里最核心的两个角色:Binder驱动和Service Manager。很多人学Binder,搞了半天分不清到底哪些工作在驱动层完成,哪些在用户空间由Service Manager处理。

打个比方:Binder驱动是高速公路,负责把数据从A点搬到B点;Service Manager就是交警+黄页,它告诉你哪条路通向哪个服务。我的经验是,把这两者的边界搞清楚,后面写Mock回放才能得心应手。

Binder驱动的核心职责

Binder驱动在Linux内核里,位置在 drivers/staging/android/binder.c。它不是普通的字符设备驱动,而是专门为进程间通信定制的。核心职责有这么几个:

职责 说明 我踩过的坑
线程池管理 驱动内部维护一个binder线程池,每个进程最多15个binder线程 项目里遇到过binder线程池耗尽,导致ANR
内存映射 通过mmap在内核空间和用户空间之间共享内存 映射大小搞错,数据拷贝效率暴跌
协议解析 解析BC(Binder Command)协议和BR(Binder Return)协议 协议头分析不仔细,Mock回放时数据错位
引用计数 管理binder对象的强引用和弱引用 引用计数泄漏,服务进程退不掉
死亡通知 当binder服务进程死亡时,通知客户端 回调没处理好,悬空指针崩溃

我个人习惯把Binder驱动看作一个内核态的代理层。客户端和服务端都不知道对方的存在,它们只跟驱动打交道。驱动负责把数据从客户端进程空间搬到一个内核缓冲区,再通知服务端来拿。说白了,底层就是一次copy_from_user加一次copy_to_user,但Binder用mmap优化成了一次拷贝。

Service Manager的角色定位

Service Manager(以下简称SM)是在用户空间跑的守护进程,服务名叫servicemanager。它是个特殊的Binder服务——它的handle是0,所以所有进程都知道怎么跟它通信。

SM的职责其实很纯粹:

  • 服务注册:服务端告诉SM「我叫张三,这是我的binder句柄」
  • 服务查找:客户端问SM「张三在哪?给我他的句柄」
  • 权限检查:判断调用方有没有权限访问某个服务

你看,SM就像是一个分布式系统中的名字服务器。它不参与实际的业务数据通信,只负责地址映射。业务数据走的是直连通道,SM只是一个中介。

踩坑提醒:SM本身也是一个Binder服务,所以它也要通过Binder驱动与客户端通信。但我曾经调试过一个案例,SM的binder线程池只有一个线程,导致多个客户端同时注册服务时出现死锁。嗯,后面Android引入了hwservicemanager才解决这个问题。

经典交互流程

下面我用一个最典型的场景来演示:服务注册和服务获取。

场景一:MediaPlayerService注册到SM

  1. MediaPlayerService启动,调用 defaultServiceManager()->addService("media.player", this)
  2. Binder驱动收到BC_TRANSACTION命令,发现目标handle=0,知道是发给SM的
  3. 驱动把数据拷贝到内核缓冲区,然后唤醒SM的binder线程
  4. SM在自己的服务表里插入一条记录:name="media.player", handle=xxx
  5. SM通过BR_REPLY返回结果,驱动再拷贝回MediaPlayerService进程

场景二:应用进程获取MediaPlayerService

  1. 应用调用 defaultServiceManager()->getService("media.player")
  2. 同样通过Binder驱动发送到SM,handle=0
  3. SM查表,找到"media.player"对应的binder引用
  4. SM把binder引用通过BR_SPAWN_LOOPER和BR_TRANSACTION返回给应用
  5. 应用拿到了binder句柄,以后直接跟MediaPlayerService通信,不再经过SM
关键点:SM只在名字解析阶段介入。一旦拿到binder句柄,后续所有IPC调用都是直接通过驱动完成的。这个设计非常高效,也是Mock回放时我们需要重点模拟的部分。

SVG:Binder驱动与Service Manager交互流程

Binder驱动与Service Manager交互流程 应用进程(Client) Binder内核驱动 Service Manager BC_TRANSACTION handle=0 唤醒SM线程 检查并插入服务表 BR_REPLY 返回结果 getService("xxx") BC_TRANSACTION 查询服务表 BR_REPLY 含binder句柄 直连调用服务 直连通信(不走SM) 业务数据直接传输 核心要点 1. SM只在注册和查询阶段介入,直连通信时无SM参与 2. 所有通信都必须经过Binder驱动,即使是SM本身 3. 驱动负责数据搬运和协议解析,SM负责名字解析和权限控制

如何验证交互是否正常

写Mock回放的时候,你要模拟的就是上面整个流程。怎么验证你模拟得对不对?

实用技巧:我一般会用 adb shell lsservice 来查看当前系统中注册的所有服务。也可以抓取binder日志:echo 1 > /sys/kernel/debug/tracing/events/binder/enable。然后在 /sys/kernel/debug/tracing/trace 里看每一条binder事务的完整细节。

拿我之前的项目举例,有一次发现Mock回放时某个服务老是返回null。查了半天,原来问题出在SM的查找阶段——我Mock的服务名大小写写错了,而SM的查找是区分大小写的。驱动层面没问题,是用户态的逻辑错了。

总结一下:

  • Binder驱动是内核代理,负责数据搬运、线程调度、引用计数
  • Service Manager是用户空间的名字服务器,只处理注册和查找
  • 两者通过handle=0的特殊通道通信
  • 一旦拿到binder句柄,后续直连通信不走SM
严重警告:Binder驱动是内核代码,一旦死锁或崩溃会导致整个系统重启。我在调试一个三方ROM时,碰到过SM的binder线程池被占满,所有新服务都注册不上,结果system_server挂死。如果要在生产环境做Binder相关改动,一定要做充分的压力和稳定性测试。

好了,这就是Binder驱动和Service Manager的全貌。下次写Mock回放逻辑时,你至少知道模拟哪一层、忽略哪一层。驱动层做数据搬运你躲不过,SM层做名字解析你可以用自己的Mock服务表替代。