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

移动开发 Android
在 Android 进程间通信(IPC)中,Binder 之所以比传统的 Socket、管道、共享内存 更高效,主要得益于其 “一次拷贝” 机制。相比传统的 两次拷贝(用户态 ⇄ 内核态 ⇄ 用户态),Binder 通过内存映射(mmap) 技术,减少了一次数据复制,从而提高了 IPC 传输效率。

在 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 传输大块数据时更加高效

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

责任编辑:武晓燕 来源: 快乐程序猿
相关推荐

2019-11-27 18:33:32

Docker架构数据

2017-07-07 11:28:24

大数据大数据技术

2023-10-09 08:24:54

IPC机制Binder

2019-04-18 10:55:00

故障演练流量

2010-03-04 09:46:51

Android Bin

2017-12-12 16:17:55

微服务系统运维

2011-11-09 15:49:52

API

2019-07-25 13:13:25

AndroidHandler消费机制

2017-05-15 19:40:40

AndroidIPC机制

2009-11-20 11:37:11

Oracle完全卸载

2019-08-08 10:18:15

运维架构技术

2016-01-08 10:03:07

硅谷通吃互联网

2010-03-30 10:44:05

Nginx启动

2024-12-04 13:52:30

2022-09-09 08:41:43

Netty服务端驱动

2021-05-24 10:55:05

Netty单机并发

2019-07-01 15:40:53

大数据架构流处理

2011-06-22 09:45:46

JavaScriptAPI

2017-11-14 08:25:36

数据库MySQL安全登陆

2018-08-07 14:45:52

编程语言JavaScripthtml
点赞
收藏

51CTO技术栈公众号