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

相关推荐
Winston Wood10 天前
Android Binder技术概览
android·binder·进程通信
红米饭配南瓜汤18 天前
Android Binder通信02 - 驱动分析 - 架构介绍
android·架构·binder
似霰1 个月前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
KeithTsui1 个月前
ZFC in LEAN 之 前集的等价关系(Equivalence on Pre-set)详解
开发语言·其他·算法·binder·swift
刘争Stanley1 个月前
深入探究安卓 Binder 机制及其应用
android·java·kotlin·binder
Dingdangr1 个月前
对Android的Binder机制的了解
android·binder
KeithTsui2 个月前
集合论(ZFC)之良创关系(Well-Founded Relation)
开发语言·其他·算法·binder·swift
千里马学框架2 个月前
安卓冻屏bug案例作业分享-千里马学员wms+input实战作业
android·车载系统·bug·binder·系统开发·launcher·taskbar
帅次2 个月前
解决 Android WebView 无法加载 H5 页面常见问题的实用指南
android·okhttp·gradle·binder·webview·retrofit·appcompat