说说binder

推荐选择方式

场景 推荐通信方式
两个组件属于同一 App Handler、Messenger、AIDL(同包)
两个 App 通信 AIDL、ContentProvider、BroadcastReceiver
进程通信大量数据 Binder 或 mmap + Socket
简单消息通知 BroadcastReceiver、Messenger
数据存储共享 ContentProvider、共享文件
先说Binder ipc通信的底层原理
再说Binder模型。

一次完整的 Binder IPC 通信过程通常是这样:

binder通信原理是一次数据拷贝+内存映射完成的

只有一块数据接收缓存区

  1. 服务端启动的时候调用mmap和binder建立内存映射通道,在服务端的用户空间开辟一块虚拟的地址空间,内核将服务端的这个虚拟空间地址记录,此时没有进行真正的内存映射
  2. 客户端发起数据传输的时候,内核才为服务端这个虚拟空间开辟物理地址空间,才真正进行了内存映射,
  3. 客户端通过系统 copyfromuser() 将数据发送给内核时,内核将客户端数据拷贝到这个地址空间,由于这个地址空间和服务端的地址空间存在内存映射,数据相当于直接发送到了服务端的地址空间。

服务端一开始就会轮训这个地址空间是否有数据,没有的话就会阻塞,有数据了,内核会唤醒服务端读数据。

服务端调用 mmap() 时:

  • 不会立刻分配 binder buffer
  • 只是在服务端进程地址空间里创建了一块虚拟内存区域(mmap 区域);
  • 这块区域的实际物理内存是由内核 binder 驱动 在"真正需要时"才分配的 ------ 即客户端发起事务时

换句话说:

服务端 mmap() 是**"预先建立通道" ,但真正的数据 buffer 是在事务发送时分配的**。

Binder 通信模型

  1. 首先,一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;
  2. Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。
  3. Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。

注意:

1,ServiceManager进程是使用BINDER_SET_CONTEXT_MGR将自己注册成ServiceManager,并会创建一个Binder 实体

2,这个 Binder 实体的引用在所有 Client 中Binder 引用编号(int)中都固定为 0 ,无需通过其它手段获得。也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManager 的 Binder 通信。

当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询自己维护的表单,一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法,并要求 B 进程把返回结果发给自己。当驱动拿到 B 进程的返回结果后就会转发给 A 进程,一次通信就完成了。

性能

  1. 性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
  2. 接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。

再说说稳定性,Binder 基于 C/S 架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好。

安全性, 服务端通过pid验证客户端,进行权限控制

内存映射

内存映射(mmap)本质上是将 「用户进程的虚拟地址空间」映射到 「一块内核控制的物理内存页」 ,从而实现内核与用户空间对同一块物理内存的共享访问


📌 更准确地说:

内存映射 并不是直接映射"虚拟地址 ↔ 虚拟地址" ,而是:

✅ 将:

  • 内核中管理的一块物理页

  • 映射到:

    • 服务端进程的用户空间虚拟地址(mmap区域)
    • 同时这块物理页也是内核中 binder buffer 的一部分

这样,内核和服务端进程就能访问同一块实际物理内存,实现高效的零拷贝通信(read/write 不需要额外 copy)。

相关推荐
cypking1 小时前
electron中IPC 渲染进程与主进程通信方法解析
前端·javascript·electron
西陵1 小时前
Nx带来极致的前端开发体验——借助playground开发提效
前端·javascript·架构
江城开朗的豌豆1 小时前
Element UI动态组件样式修改小妙招,轻松拿捏!
前端·javascript·vue.js
float_六七2 小时前
JavaScript:现代Web开发的核心动力
开发语言·前端·javascript
zhaoyang03012 小时前
vue3笔记(2)自用
前端·javascript·笔记
德育处主任Pro2 小时前
# JsSIP 从入门到实战:构建你的第一个 Web 电话
前端
拾光拾趣录3 小时前
setTimeout(1) 和 setTimeout(2) 的区别
前端·v8
拾光拾趣录3 小时前
内存泄漏的“隐形杀手”
前端·性能优化
摸鱼仙人~3 小时前
HttpServletRequest深度解析:Java Web开发的核心组件
java·开发语言·前端
索西引擎3 小时前
【工程化】浅谈前端构建工具
前端·webpack·gulp·turbopack