在 Android 进程间通信(IPC)中,Binder 之所以比传统的 Socket、管道、共享内存 更高效,主要得益于其 “一次拷贝” 机制。相比传统的 两次拷贝(用户态 ⇄ 内核态 ⇄ 用户态),Binder 通过内存映射(mmap) 技术,减少了一次数据复制,从而提高了 IPC 传输效率。
那么,Binder 的“一次拷贝”是如何实现的?本文将结合 流程图 + 代码解析,带你彻底搞懂这个关键技术!🚀
1.Binder 通信数据拷贝流程
通常,进程间通信需要经历 发送进程 → 内核 → 接收进程 这样一个传输链路。我们先看一下常见的 两次拷贝 方式,再看看 Binder 如何优化为“一次拷贝”。
(1) 传统 IPC 的“两次拷贝”
在 Socket 或管道等传统 IPC 方式中,数据需要 两次拷贝:
1️⃣ 发送进程 → 内核态缓冲区(第一次拷贝)
2️⃣ 内核态缓冲区 → 目标进程(第二次拷贝)
📌 数据传输流程图(两次拷贝)
图片
缺点:数据需要 两次拷贝,会产生额外的 CPU 及内存开销
问题:如何减少一次拷贝,提高 IPC 传输效率?
(2) Binder 的“一次拷贝”优化
Binder 发送进程(Client) 向 接收进程(Server) 发送数据,数据 仅拷贝一次,避免传统 IPC 的 两次拷贝(用户态 ⇄ 内核态 ⇄ 用户态)。
📍Step 1:发送进程 mmap 共享内存
发送进程(Client) 通过 mmap() 在自己的用户空间创建 Binder Buffer,这块内存由 Binder 驱动管理。
📍Step 2:发送进程向 Binder 驱动发起请求
📌 发送进程(Client)向 Binder 驱动提交数据:
数据写入 mmap 共享内存
Binder 驱动解析目标进程
Binder 驱动查找 Server 的 mmap 共享区域
📍Step 3:Binder 驱动将数据拷贝到目标进程的 mmap 共享区
📌 关键点:
Binder 驱动查找 Server 进程的 mmap 共享内存
Binder 直接将数据拷贝到 Server 进程的共享 Buffer
Server 进程 直接 读取共享 Buffer
📌 示意图:
图片
💡 优化点:
只拷贝一次(Client → Server mmap),避免传统 IPC 需要两次拷贝
Server 进程 无需额外拷贝,直接在 mmap 共享内存中读取数据
📍Step 4:Server 读取数据
📌 Server 进程直接读取 mmap 共享 Buffer,不需要额外拷贝:
2.一次拷贝的关键技术:Binder Buffer
Binder 之所以能做到“一次拷贝”,关键在于 Binder Buffer 机制。
📌 核心技术点:
✅ mmap 映射共享内存:Binder 通过 mmap 预先在用户空间 分配一块共享缓冲区(Binder Buffer),让数据可以在进程间直接拷贝。
✅ 物理页共享:Binder 驱动在内核中管理物理页,把这块缓冲区映射到不同进程的地址空间,实现跨进程访问。
✅ 用户态拷贝数据:数据直接从 发送进程 拷贝到 接收进程的映射缓冲区,不再经过内核缓冲区。
mmap 共享缓冲区示意图
图片
作用:这块 mmap 共享内存 让多个进程能共享相同的物理内存页,避免了不必要的数据拷贝
关键点:数据不会进入内核缓冲区,而是直接 从发送进程拷贝到接收进程
3.一次拷贝的核心实现(代码解析)
Binder Buffer 的实现,主要涉及 mmap 共享内存映射 + copy_from_user() 数据拷贝。
📌 (1) 发送进程:申请 Binder Buffer
int binder_fd = open("/dev/binder", O_RDWR);
size_t buffer_size = 128 * 1024; // 128 KB
mmap(NULL, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, binder_fd, 0);
通过 mmap() 分配一块共享缓冲区,这块缓冲区由 Binder 驱动管理
📌 (2) 发送数据时:数据直接拷贝到目标进程的映射内存
void binder_transact(int binder_fd, void* data, size_t len) {
struct binder_write_read bwr;
bwr.write_size = len;
bwr.write_buffer = (uintptr_t)data;
ioctl(binder_fd, BINDER_WRITE_READ, &bwr);
}
binder_write_read 结构体用于 指向共享缓冲区,内核直接操作这个地址进行数据传输
📌 (3) 内核拷贝数据到目标进程(一次拷贝实现)
copy_from_user(mapped_buffer, user_buffer, size);
copy_from_user() 直接拷贝数据到目标进程的映射区域
4.总结:Binder 为什么比传统 IPC 高效?
✅ Binder 通过 mmap 共享内存,避免两次拷贝,提升 IPC 速度
✅ Binder 通过物理页映射,让数据直接在用户空间共享
✅ 相比传统 IPC(Socket、管道),Binder 传输大块数据时更加高效
本文转载自微信公众号「 快乐程序猿」,可以通过以下二维码关注。转载本文请联系快乐程序猿公众号。