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只是一个中介。
hwservicemanager才解决这个问题。
经典交互流程
下面我用一个最典型的场景来演示:服务注册和服务获取。
场景一:MediaPlayerService注册到SM
- MediaPlayerService启动,调用
defaultServiceManager()->addService("media.player", this) - Binder驱动收到BC_TRANSACTION命令,发现目标handle=0,知道是发给SM的
- 驱动把数据拷贝到内核缓冲区,然后唤醒SM的binder线程
- SM在自己的服务表里插入一条记录:name="media.player", handle=xxx
- SM通过BR_REPLY返回结果,驱动再拷贝回MediaPlayerService进程
场景二:应用进程获取MediaPlayerService
- 应用调用
defaultServiceManager()->getService("media.player") - 同样通过Binder驱动发送到SM,handle=0
- SM查表,找到"media.player"对应的binder引用
- SM把binder引用通过BR_SPAWN_LOOPER和BR_TRANSACTION返回给应用
- 应用拿到了binder句柄,以后直接跟MediaPlayerService通信,不再经过SM
SVG:Binder驱动与Service Manager交互流程
如何验证交互是否正常
写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
system_server挂死。如果要在生产环境做Binder相关改动,一定要做充分的压力和稳定性测试。
好了,这就是Binder驱动和Service Manager的全貌。下次写Mock回放逻辑时,你至少知道模拟哪一层、忽略哪一层。驱动层做数据搬运你躲不过,SM层做名字解析你可以用自己的Mock服务表替代。