Binder架构

一、架构

如上图,binder 分为用户层和驱动层两部分,用户层有客户端(Client)、服务端(Server)、服务管理(ServiceManager)。

从用户空间的角度,使用步骤如下(三者关系):

  • Server 往 ServiceManager 注册服务;
  • Client 从 ServiceManager 获取服务;
  • Client 使用获取到服务功能。

但其实,三者并不是直接通信的,而是各自调用 binder 驱动(ioctl),由 binder 驱动来做通信转发。这一部分是封装在 Binder API 里的,应用使用时完全不需要关心。

为什么这么设计?

比如,我们开发有一个进程A,想开发一个功能,让手机的手电筒打开;但是,手电筒只有进程B的 OpenLed 函数可以控制;这在商业项目中非常常见,模块高内聚,低耦合,职责非常单一。要做到进程A直接调用进程B的 OpenLed 函数(前面说的RPC),这个时候,进程A就是Client,进程B是Server;进程A,首先找到Binder的大管家ServiceManager获取进程B的句柄handle(进程B提前已经告诉(注册)大管家自己的句柄id是啥了),接着,进程A将 OpenLed 需要的参数进行打包封装,然后通过ioctl拷贝给内核里面的 Binder驱动,驱动拿到这些数据之后,寻思着这是发给谁的呢?一看数据包裹里面的handle就明白了,于是将数据转给(这儿其实是mmap,先有点印象别计较)进程B,进程B拿到之后,看看进程A是想调用我哪个函数?里面有个code给函数编号了,于是乎,进程B就去调用自己本地的OpenLed ,手电筒就打开了。

当然,进程B打开手电筒之后还可以将函数的执行结果信息(返回值,出参啥的)返回给进程A,这个不影响理解,先不管。

下面看看这三个进程分别干了什么:

Client进程:

open驱动;

获取服务(向ServiceManager查询服务,获得一个handle);

向handle发数据;

ServiceManager进程:(下文简写为sm)

open驱动;

告诉驱动,我是service manager,我是大管家,以后所有的服务要想使用binder,

必须告诉我他们叫啥,handle是多少。

然后就是循环:

从驱动读取数据;

解析数据;

处理数据;其实我们使用sm的功能相对单一,就是两种:注册服务

(将服务名添加到自己的链表中)和获取服务(从链表查找服务,返回服务handle);

Server进程:

open驱动;

注册服务;将自己的服务名发给大管家sm;

循环:

从驱动读取数据;

解析数据,并调用对应的函数;

返回数据;

二、Binder的作用

到此,我们理解了 CS 架构的优点,以及它的大致原理,但是可能会产生一个疑问:

CS架构看起来并不是和 Binder 强绑定的,使用任意的进程通信方法都可以支撑这个CS架构,那为啥非 Binder 不可呢?

原因就是两个字:效率。

这里包含了运行效率和使用效率。

运行效率高,指的是 Binder 仅需要进行一次数据拷贝。什么意思呢?通常来说,进程间的通信都需要拷贝两次数据:

"进程A --(拷贝到)-- 内核","内核 --(拷贝到)-- 进程B"。

而 Binder 的 Server 端,用户空间和内核空间的是做了内存映射的,所以内核和 Server 应用之间的拷贝就可以省了。别小看这一次拷贝,这在 Binder 作为主力 IPC 方法而被大量频繁调用的安卓系统中,节省下来的开销还是很客观的。

相信有人可能又有疑惑了,那共享内存不是更节省开销吗,共享内存一次拷贝都不需要了。这里就不得不提到第二点"使用效率"了。

贡献内存最大的缺点之一就是难以同步,这得在两端约定一套协议规则才行,并且这在服务端对应多个客户端的时候,变得更加麻烦。

相信没人希望没实现一个功能,就要对大量的基础通信的开发调试吧?

所以,Binder 帮大家做好了,Binder 就像是是"半个共享内存 + 进程同步管理"。

三、通信原理

1、Binder底层使用到mmap

Binder是基于内存映射mmap设计实现的,通过这种方式,直接操作映射的这一部分内存,通过mmap,Binder通信时,只需要经历一次数据复制,从而获得更好的性能。

2、一次Binder IPC通信的过程分为以下几个步骤:

首先,Binder驱动在内核空间中开辟出一个数据接收缓冲区

接着,在内核空间中开辟出一个内核缓冲区

将内核缓冲区与数据接收缓冲区建立映射关系

将数据接收缓冲区与接收进程的用户空间地址建立映射关系

发送方进程通过copy_from_user将数据从用户空间复制到内核缓冲区

由于内核缓冲区与数据接收缓冲区有映射关系,同时数据接收缓冲区与接收进程的用户空间地址有映射关系,所以在接收进程中可以直接获取到这段数据

3、这样便完成了一次Binder IPC通信,它的原理如下图所示:

相关推荐
每次的天空10 天前
Android学习总结之Binder篇
android·学习·binder
Bonnie_cat2 个月前
Android Framwork 之深入理解 IPC Binder机制
android·binder
Mekeater3 个月前
手写一个Java Android Binder服务及源码分析
android·java·binder·proxy·stub·mremote·bbinder
Mekeater3 个月前
手写一个C++ Android Binder服务及源码分析
android·c++·binder·remote·iservicemanager·bpbinder·bprefbase
aaajj4 个月前
【Android】直接使用binder的transact来代替aidl接口
android·binder
明天就是Friday5 个月前
Android Binder 进程间通信
android·binder
冬瓜神君5 个月前
Android14 AOSP 允许system分区和vendor分区应用进行AIDL通信
android·binder
小狗爱世界5 个月前
深入解析Binder源码
android·binder
Winston Wood6 个月前
Android Binder技术概览
android·binder·进程通信