Android Binder 如何做到 IPC 只拷贝一次?mmap 机制全解!

在 Android 进程间通信(IPC)中,Binder 之所以比传统的 Socket、管道、共享内
首页 新闻资讯 行业资讯 Android Binder 如何做到 IPC 只拷贝一次?mmap 机制全解!

在 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

intbinder_fd=open("/dev/binder",O_RDWR);size_t buffer_size=128*1024;// 128 KBmmap(NULL,buffer_size,PROT_READ|PROT_WRITE,MAP_SHARED,binder_fd,0);

通过 mmap() 分配一块共享缓冲区,这块缓冲区由 Binder 驱动管理

📌 (2) 发送数据时:数据直接拷贝到目标进程的映射内存

void binder_transact(intbinder_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 传输大块数据时更加高效

本文转载自微信公众号「 快乐程序猿」,可以通过以下二维码关注。转载本文请联系快乐程序猿公众号。

478805613a4346b6cbd0673a3a302a3b821bf3.jpg

103    2025-03-10 00:35:00    Android IPC 管道