3、Binder 架构总览:Client、Server、ServiceManager、Binder Driver 四要素,以及它们如何协作
好,我们直接进入正题。
Binder 架构,说白了就是四个角色在跳舞:Client、Server、ServiceManager、Binder Driver。你想想看,一个跨进程通信,如果没有一个清晰的架构,那不乱成一锅粥?Android 的设计者很聪明,他们把这四个角色分得清清楚楚,各司其职。
四个角色,各管一摊
我先用一句话概括每个角色的职责,然后再细聊。
- Client:服务的消费者。它想调用某个服务,比如获取系统时间、读取联系人。
- Server:服务的提供者。它实现了具体的功能,等着别人来调用。
- ServiceManager:服务的大管家。它负责注册服务、查询服务。你可以把它理解成一个电话本。
- Binder Driver:内核里的搬运工。所有跨进程的数据传输,最终都要经过它。
嗯,这里要注意:Binder Driver 是内核模块,不是用户态的代码。它工作在 Linux 内核空间,Client、Server、ServiceManager 都是用户态的进程。这个分层设计,保证了安全性和稳定性。
核心要点:Binder 通信的本质,是用户态进程通过 ioctl 系统调用,与内核态的 Binder Driver 交互。Driver 负责数据拷贝、线程管理、身份校验。
它们是怎么协作的?
我个人习惯用一个生活场景来理解:你去酒店开房。
- ServiceManager 就是前台。它知道每个房间(服务)在哪里。
- Server 是住客,它入住时告诉前台自己的房间号(注册服务)。
- Client 是访客,它到前台问:“我要找 301 房间”(查询服务)。
- Binder Driver 是酒店的内部电话系统。访客和住客不直接见面,所有通话都通过电话交换机(Driver)转接。
你看,这样一比喻,是不是清晰多了?
协作流程:三步走
整个协作过程,我把它拆成三步。每一步都有讲究。
第一步:Server 注册服务
Server 进程启动后,会向 ServiceManager 注册自己。它要告诉大管家:“我叫某某服务,这是我的 Binder 引用,以后别人找我,就通过这个引用。”
注册的过程,其实也是一次 Binder 通信。Server 把注册请求发给 Binder Driver,Driver 再转给 ServiceManager。ServiceManager 把服务名和 Binder 引用存到自己的查找表里。
避坑指南:我曾经遇到过一个问题,服务注册成功了,但 Client 就是查不到。后来发现是 ServiceManager 的查找表满了。Android 对 ServiceManager 能管理的服务数量有限制,默认好像是 128 个。如果你的系统服务特别多,要注意这个上限。
第二步:Client 获取服务
Client 要使用某个服务时,它先问 ServiceManager:“请问 XXX 服务在哪儿?”
ServiceManager 查一下自己的表,如果找到了,就把 Server 的 Binder 引用返回给 Client。注意,这里返回的不是真正的 Server 进程地址,而是一个代理对象(BpBinder)。Client 拿着这个代理对象,就可以跟 Server 通信了。
为什么是代理?因为 Client 和 Server 不在同一个进程,不能直接调用。代理对象负责把调用请求打包,发给 Binder Driver,再由 Driver 转发给 Server。
第三步:Client 调用 Server 的方法
这是最核心的一步。Client 通过代理对象发起调用,流程如下:
- Client 调用代理对象的方法,比如
transact()。 - 代理对象把调用数据(方法名、参数等)打包成
Parcel。 - 代理对象通过 ioctl 把 Parcel 发给 Binder Driver。
- Binder Driver 检查权限,找到目标 Server 的 Binder 实体。
- Driver 把数据拷贝到 Server 进程的内核空间,再通知 Server 进程来取。
- Server 的 Binder 线程池收到请求,解包 Parcel,执行对应方法。
- 执行结果原路返回给 Client。
整个过程,数据只拷贝一次。这是 Binder 比传统 Socket 高效的地方。传统方式要拷贝两次,Binder 利用内核的 mmap 机制,做到了“一次拷贝”。
注意:Binder 通信是同步的。Client 调用 transact() 后会阻塞,直到 Server 返回结果。如果你的 Server 处理时间很长,Client 就会卡住。我建议在耗时操作中使用异步 Binder 调用,或者把耗时任务放到 Server 的独立线程里。
一张图看懂 Binder 架构
下面这张 SVG 图,把四个角色和协作流程画出来了。我建议你仔细看看,对照上面的文字理解。
ServiceManager 的特殊地位
你可能注意到了,ServiceManager 本身也是一个 Server。但它比较特殊,它是系统启动时第一个启动的 Binder 服务。其他所有服务都要向它注册。
那问题来了:ServiceManager 自己怎么被找到?
答案是:Binder Driver 认识它。ServiceManager 在启动时,会通过 BINDER_SET_CONTEXT_MGR 这个 ioctl 命令,把自己注册到 Driver 里。Driver 内部有一个特殊标记,专门指向 ServiceManager。从此以后,任何进程想查询服务,Driver 都知道该把请求发给谁。
个人经验:我记得有一次调试一个系统服务,发现 ServiceManager 进程挂了,结果所有 Binder 通信都瘫痪了。因为新启动的服务无法注册,Client 也查不到任何服务。所以,ServiceManager 是 Binder 体系的基石,它必须稳定运行。Android 系统对 ServiceManager 有保活机制,但如果你在定制系统,一定要小心别误杀了它。
Binder Driver 的隐藏技能
Binder Driver 不只是个搬运工。它还做了几件重要的事:
- 权限校验:每次通信,Driver 都会检查调用方的 UID/PID,确保它有权限访问目标服务。
- 线程管理:Driver 维护了一个 Binder 线程池。当有请求进来时,它会从池子里分配一个线程去处理。如果线程不够,它会通知 Server 进程创建新线程。
- 死亡通知:如果 Server 进程挂了,Driver 会通知所有依赖它的 Client。这样 Client 可以及时处理,避免死等。
嗯,这里要注意:Binder 线程池的大小是有限的。默认是 16 个线程。如果你的 Server 同时处理大量请求,线程池可能会被占满,新的请求就得排队。我曾经在项目中遇到过这个问题,后来通过调整线程池大小和优化请求处理逻辑解决了。
总结一下
Binder 架构的四要素,各司其职:
- Client 发起请求,Server 处理请求。
- ServiceManager 负责服务注册和查询,是服务的“电话本”。
- Binder Driver 是内核里的核心,负责数据搬运、权限校验、线程管理。
协作流程就是三步:注册、查询、调用。每一步都离不开 Binder Driver 的参与。理解了这四个角色和它们的协作方式,你就掌握了 Binder 架构的骨架。后面我们再往骨架上填血肉,比如 Binder 协议、内存映射、线程模型等等。
一句话记住:Binder 架构 = 三个用户态进程 + 一个内核模块。进程间不直接通信,全部通过内核模块中转。
公众号:蓝海资料掘金营,微信deep3321