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通信,它的原理如下图所示:

相关推荐
aaajj3 小时前
【Android】直接使用binder的transact来代替aidl接口
android·binder
明天就是Friday21 天前
Android Binder 进程间通信
android·binder
冬瓜神君1 个月前
Android14 AOSP 允许system分区和vendor分区应用进行AIDL通信
android·binder
小狗爱世界1 个月前
深入解析Binder源码
android·binder
Winston Wood2 个月前
Android Binder技术概览
android·binder·进程通信
红米饭配南瓜汤2 个月前
Android Binder通信02 - 驱动分析 - 架构介绍
android·架构·binder
似霰2 个月前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
KeithTsui2 个月前
ZFC in LEAN 之 前集的等价关系(Equivalence on Pre-set)详解
开发语言·其他·算法·binder·swift
刘争Stanley3 个月前
深入探究安卓 Binder 机制及其应用
android·java·kotlin·binder