3、Binder 架构总览:Client、Server、ServiceManager、Binder Driver 四要素,以及它们如何协作

好,我们直接进入正题。

Binder 架构,说白了就是四个角色在跳舞:ClientServerServiceManagerBinder 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 通过代理对象发起调用,流程如下:

  1. Client 调用代理对象的方法,比如 transact()
  2. 代理对象把调用数据(方法名、参数等)打包成 Parcel
  3. 代理对象通过 ioctl 把 Parcel 发给 Binder Driver。
  4. Binder Driver 检查权限,找到目标 Server 的 Binder 实体。
  5. Driver 把数据拷贝到 Server 进程的内核空间,再通知 Server 进程来取。
  6. Server 的 Binder 线程池收到请求,解包 Parcel,执行对应方法。
  7. 执行结果原路返回给 Client。

整个过程,数据只拷贝一次。这是 Binder 比传统 Socket 高效的地方。传统方式要拷贝两次,Binder 利用内核的 mmap 机制,做到了“一次拷贝”。

注意:Binder 通信是同步的。Client 调用 transact() 后会阻塞,直到 Server 返回结果。如果你的 Server 处理时间很长,Client 就会卡住。我建议在耗时操作中使用异步 Binder 调用,或者把耗时任务放到 Server 的独立线程里。

一张图看懂 Binder 架构

下面这张 SVG 图,把四个角色和协作流程画出来了。我建议你仔细看看,对照上面的文字理解。

Binder 架构四要素协作图 用户态 (User Space) Client 服务消费者 ServiceManager 服务大管家 Server 服务提供者 内核态 (Kernel Space) Binder Driver 数据搬运工 / 权限校验 Client 请求 Server 响应 服务注册/查询

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