Binder机制

Binder概述

Binder是Android专有的一种IPC机制,它基于开源的OpenBinder实现。相比Linux已有的IPC方式(如管道、消息队列、共享内存等),Binder具有以下优势:

  • 性能高效:只需一次数据拷贝
  • 安全性好:为每个App分配UID,进行有效性检测
  • 使用简单:采用面向对象的思想,调用方式类似调用本地对象

Binder架构

Binder采用C/S架构,主要由以下几部分组成:

  • Client:使用服务的客户端
  • Server:提供服务的服务端
  • ServiceManager:管理服务的注册和查询
  • Binder驱动:运行在内核空间,负责进程间Binder通信的建立

image-20241019231201444

Binder通信原理

Binder的通信过程大致如下:

  1. Server向ServiceManager注册服务
  2. Client向ServiceManager查询服务
  3. ServiceManager返回服务的Binder代理给Client
  4. Client通过Binder代理发起远程调用
  5. Binder驱动将数据从Client进程拷贝到Server进程
  6. Server处理请求,并将结果返回

整个过程中,Binder驱动起着至关重要的作用,它利用内存映射(mmap)来实现零拷贝,大大提高了效率。

  • 内存映射的建立:

    • 当一个进程首次使用Binder时,会通过mmap系统调用向Binder驱动申请一块内存12。Binder驱动会在内核空间分配一块物理内存,然后将其映射到用户空间和内核空间的虚拟地址
  • 零拷贝的实现:

    • 发送数据时,发送进程将数据写入映射的用户空间内存
    • 由于该内存同时映射到内核空间,Binder驱动可以直接访问这些数据,无需额外拷贝
    • 接收进程的用户空间也映射了同一块物理内存,因此可以直接读取数据,再次避免了拷贝

这种机制将传统IPC的两次拷贝(用户空间到内核空间,再到另一个用户空间)减少为一次,大大提高了数据传输效率

image-20241019140002597

Binder在Android中的应用

Binder机制在Android系统中应用广泛,主要体现在:

  • 系统服务:如AMS、WMS等都基于Binder实现
  • 四大组件:组件间的通信都依赖Binder
  • AIDL:跨进程接口定义语言,底层就是Binder
  • ContentProvider:底层通过Binder实现数据共享

Binder相比其他IPC机制的优势

Binder 共享内存 Socket
性能 需要拷贝一次 无需拷贝 需要拷贝两次
特点 基于C/S架构,易用性高 控制复杂,易用性差 基于C/S 架构 作为一款通用接口,其传输 效率低,开销大
安全性 为每个APP分配UID,同时支持实名和匿名 依赖上层协议,访问接入点是开放的,不安全 依赖上层协议,访问接入点是开放的,不安全

Binder 如何做到一次拷贝?

内核空间通过系统调用将A进程用户空间的数据复制一份到内核空间(copy from user);

内核空间的虚拟内存 最终都必须要关联到同一个物理内存「所有app的内核空间都映射同一个物理空间」(利用 MMU,将虚拟内存转换成物理内存);

内核空间将数据放到内核空间的虚拟内存,内核空间的这个虚拟内存映射的物理内存地址和接收方 B 进程的用户空间中虚拟内存映射的物理内存地址是同一个(如何映射:通过 mmap(零拷贝技术) 来完成映射);

内核往里面放,就相当于放到了 B 进程用户空间的虚拟内存中,B 进程可直接访问;

image-20241019142710571

还不明白看下文

而在Binder中,整体通信机制和传统IPC一样,那么为何能实现一次copy,这是因为在物理内存的指向上做了改动。

在Binder中,内核空间和进程2中分别另外开辟了一块虚拟内存,这两块虚拟内存指向物理内存中的同一块内存空间。然后传递数据的时候,将数据从进程1的物理内存1中复制到内核空间和进程2共同指向的物理内存块中,也就是下图物理内存中的紫色部分。这样进程2中就获取到了数据。这样就实现了一次copy。

image-20241019230406912

MMAP原理

Linux通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping)。

对文件进行mmap,会在进程的虚拟内存分配地址空间,创建映射关系。实现这样的映射关系后,就可以采用指针的方式读写操作这一段内存,而系统会自动回写到对应的文件磁盘上。

image-20241019210009838

写文件流程:

1. 调用write,告诉内核需要写入数据的开始地址与长度 。
2. 内核将数据拷贝到内核缓存 。
3. 由操作系统调用,将数据拷贝到磁盘,完成写入

image-20241019211331834

参考文档:

  1. https://yangjie2.github.io/2021/11/14/mmap%E5%8E%9F%E7%90%86%E4%B8%8E%E5%BA%94%E7%94%A8/
  2. https://juejin.cn/post/7110925904322232327#heading-6