Android Binder 如何做到 IPC 只拷貝一次?mmap 機制全解!
在 Android 進程間通信(IPC)中,Binder 之所以比傳統(tǒng)的 Socket、管道、共享內(nèi)存 更高效,主要得益于其 “一次拷貝” 機制。相比傳統(tǒng)的 兩次拷貝(用戶態(tài) ? 內(nèi)核態(tài) ? 用戶態(tài)),Binder 通過內(nèi)存映射(mmap) 技術,減少了一次數(shù)據(jù)復制,從而提高了 IPC 傳輸效率。
那么,Binder 的“一次拷貝”是如何實現(xiàn)的?本文將結(jié)合 流程圖 + 代碼解析,帶你徹底搞懂這個關鍵技術!??
1.Binder 通信數(shù)據(jù)拷貝流程
通常,進程間通信需要經(jīng)歷 發(fā)送進程 → 內(nèi)核 → 接收進程 這樣一個傳輸鏈路。我們先看一下常見的 兩次拷貝 方式,再看看 Binder 如何優(yōu)化為“一次拷貝”。
(1) 傳統(tǒng) IPC 的“兩次拷貝”
在 Socket 或管道等傳統(tǒng) IPC 方式中,數(shù)據(jù)需要 兩次拷貝:
1?? 發(fā)送進程 → 內(nèi)核態(tài)緩沖區(qū)(第一次拷貝)
2?? 內(nèi)核態(tài)緩沖區(qū) → 目標進程(第二次拷貝)
?? 數(shù)據(jù)傳輸流程圖(兩次拷貝)
圖片
缺點:數(shù)據(jù)需要 兩次拷貝,會產(chǎn)生額外的 CPU 及內(nèi)存開銷
問題:如何減少一次拷貝,提高 IPC 傳輸效率?
(2) Binder 的“一次拷貝”優(yōu)化
Binder 發(fā)送進程(Client) 向 接收進程(Server) 發(fā)送數(shù)據(jù),數(shù)據(jù) 僅拷貝一次,避免傳統(tǒng) IPC 的 兩次拷貝(用戶態(tài) ? 內(nèi)核態(tài) ? 用戶態(tài))。
??Step 1:發(fā)送進程 mmap 共享內(nèi)存
發(fā)送進程(Client) 通過 mmap() 在自己的用戶空間創(chuàng)建 Binder Buffer,這塊內(nèi)存由 Binder 驅(qū)動管理。
??Step 2:發(fā)送進程向 Binder 驅(qū)動發(fā)起請求
?? 發(fā)送進程(Client)向 Binder 驅(qū)動提交數(shù)據(jù):
數(shù)據(jù)寫入 mmap 共享內(nèi)存
Binder 驅(qū)動解析目標進程
Binder 驅(qū)動查找 Server 的 mmap 共享區(qū)域
??Step 3:Binder 驅(qū)動將數(shù)據(jù)拷貝到目標進程的 mmap 共享區(qū)
?? 關鍵點:
Binder 驅(qū)動查找 Server 進程的 mmap 共享內(nèi)存
Binder 直接將數(shù)據(jù)拷貝到 Server 進程的共享 Buffer
Server 進程 直接 讀取共享 Buffer
?? 示意圖:
圖片
?? 優(yōu)化點:
只拷貝一次(Client → Server mmap),避免傳統(tǒng) IPC 需要兩次拷貝
Server 進程 無需額外拷貝,直接在 mmap 共享內(nèi)存中讀取數(shù)據(jù)
??Step 4:Server 讀取數(shù)據(jù)
?? Server 進程直接讀取 mmap 共享 Buffer,不需要額外拷貝:
2.一次拷貝的關鍵技術:Binder Buffer
Binder 之所以能做到“一次拷貝”,關鍵在于 Binder Buffer 機制。
?? 核心技術點:
? mmap 映射共享內(nèi)存:Binder 通過 mmap 預先在用戶空間 分配一塊共享緩沖區(qū)(Binder Buffer),讓數(shù)據(jù)可以在進程間直接拷貝。
? 物理頁共享:Binder 驅(qū)動在內(nèi)核中管理物理頁,把這塊緩沖區(qū)映射到不同進程的地址空間,實現(xiàn)跨進程訪問。
? 用戶態(tài)拷貝數(shù)據(jù):數(shù)據(jù)直接從 發(fā)送進程 拷貝到 接收進程的映射緩沖區(qū),不再經(jīng)過內(nèi)核緩沖區(qū)。
mmap 共享緩沖區(qū)示意圖
圖片
作用:這塊 mmap 共享內(nèi)存 讓多個進程能共享相同的物理內(nèi)存頁,避免了不必要的數(shù)據(jù)拷貝
關鍵點:數(shù)據(jù)不會進入內(nèi)核緩沖區(qū),而是直接 從發(fā)送進程拷貝到接收進程
3.一次拷貝的核心實現(xiàn)(代碼解析)
Binder Buffer 的實現(xiàn),主要涉及 mmap 共享內(nèi)存映射 + copy_from_user() 數(shù)據(jù)拷貝。
?? (1) 發(fā)送進程:申請 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() 分配一塊共享緩沖區(qū),這塊緩沖區(qū)由 Binder 驅(qū)動管理
?? (2) 發(fā)送數(shù)據(jù)時:數(shù)據(jù)直接拷貝到目標進程的映射內(nèi)存
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 結(jié)構(gòu)體用于 指向共享緩沖區(qū),內(nèi)核直接操作這個地址進行數(shù)據(jù)傳輸
?? (3) 內(nèi)核拷貝數(shù)據(jù)到目標進程(一次拷貝實現(xiàn))
copy_from_user(mapped_buffer, user_buffer, size);
copy_from_user() 直接拷貝數(shù)據(jù)到目標進程的映射區(qū)域
4.總結(jié):Binder 為什么比傳統(tǒng) IPC 高效?
? Binder 通過 mmap 共享內(nèi)存,避免兩次拷貝,提升 IPC 速度
? Binder 通過物理頁映射,讓數(shù)據(jù)直接在用戶空間共享
? 相比傳統(tǒng) IPC(Socket、管道),Binder 傳輸大塊數(shù)據(jù)時更加高效
本文轉(zhuǎn)載自微信公眾號「 快樂程序猿」,可以通過以下二維碼關注。轉(zhuǎn)載本文請聯(lián)系快樂程序猿公眾號。