先從內(nèi)核來總結(jié)下Binder驅(qū)動,從Binder IPC原理我們就知道了,對于進(jìn)程來說,用戶空間是不能共享的,而內(nèi)核空間卻可以。所以只能費(fèi)這么大勁,在內(nèi)核設(shè)計(jì)一個(gè)Binder驅(qū)動來間接打通client和server進(jìn)程的通信。另外,Binder驅(qū)動是Android專用的,盡管名叫“驅(qū)動“,實(shí)際上和硬件設(shè)備沒有任何關(guān)系,只是實(shí)現(xiàn)方式和設(shè)備驅(qū)動程序是一樣的而已。
一、系統(tǒng)調(diào)用
在學(xué)習(xí)Binder Driver之前,先了解下系統(tǒng)調(diào)用流程:用戶態(tài)的程序調(diào)用Kernel層驅(qū)動是需要陷入內(nèi)核態(tài),進(jìn)行系統(tǒng)調(diào)用(syscall),比如打開Binder驅(qū)動方法的調(diào)用鏈為: open()(用戶空間方法)-> __open()(systemCall對應(yīng)方法)-> binder_open()(binder 驅(qū)動方法)。至于其他的從用戶態(tài)陷入內(nèi)核態(tài)的流程也基本一致。
二、主要工作函數(shù)介紹
binder_init:注冊misc設(shè)備,對應(yīng)于miscdevice結(jié)構(gòu)體。這個(gè)不多說,簡單說就是驅(qū)動的注冊以及一些初始化工作。
binder_open:創(chuàng)建binder_proc對象,并把當(dāng)前進(jìn)程等信息保存到binder_proc對象,該對象管理IPC所需的各種信息并擁有其他結(jié)構(gòu)體的根結(jié)構(gòu)體;再把binder_proc對象保存到文件指針filp,以及把binder_proc加入到全局鏈表binder_procs。(Binder驅(qū)動中通過static HLIST_HEAD(binder_procs)創(chuàng)建了全局的哈希鏈表binder_procs。)
binder_mmap: 首先在內(nèi)核虛擬地址空間,申請一塊與用戶虛擬內(nèi)存相同大小的內(nèi)存;然后再申請1個(gè)page大小的物理內(nèi)存,再將同一塊物理內(nèi)存分別映射到內(nèi)核虛擬地址空間和用戶虛擬內(nèi)存空間,從而實(shí)現(xiàn)了用戶空間的Buffer和內(nèi)核空間的Buffer同步操作的功能。
binder_ioctl: 針對不同的ioctl命令在兩個(gè)進(jìn)程間收發(fā)IPC數(shù)據(jù)和IPC reply數(shù)據(jù)。
通過switch接收不同的命令,對應(yīng)執(zhí)行不同的操作,代碼如下:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
//參數(shù)arg表示用戶空間傳過來的數(shù)據(jù)
{
...
switch (cmd) {
case BINDER_WRITE_READ: //進(jìn)行binder的讀寫操作
ret = binder_ioctl_write_read(filp, cmd, arg, thread); //【見2.4.2】
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS: //設(shè)置binder最大支持的線程數(shù)
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR: //成為binder的上下文管理者,也就是ServiceManager成為守護(hù)進(jìn)程
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT: //當(dāng)binder線程退出,釋放binder線程
binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: { //獲取binder的版本號
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
...
return ret;
}
三、Binder協(xié)議
Binder協(xié)議可以分為控制協(xié)議和驅(qū)動協(xié)議兩類。
控制協(xié)議:用戶空間進(jìn)程通過ioctl與Binder驅(qū)動進(jìn)行通信的協(xié)議,也叫做ioctl命令。
主要控制協(xié)議如下:
| ioctl命令 | 操作 | 使用場景 |
|---|---|---|
| BINDER_WRITE_READ | 收發(fā)Binder IPC數(shù)據(jù) | Binder讀寫交互場景,IPC.talkWithDriver |
| BINDER_SET_MAX_THREADS | 設(shè)置Binder線程最大個(gè)數(shù) | servicemanager進(jìn)程成為上下文管理者,binder_become_context_manager() |
| BINDER_SET_CONTEXT_MGR | 設(shè)置Service Manager節(jié)點(diǎn) | 初始化ProcessState對象,open_driver()主動調(diào)整參數(shù),ProcessState.setThreadPoolMaxThreadCount() |
驅(qū)動協(xié)議:描述了對于Binder驅(qū)動的具體使用過程。
驅(qū)動協(xié)議又可以分為兩類:
BINDER_COMMAND_PROTOCOL:binder請求碼,以”BC_“開頭,簡稱BC碼,用于從IPC層傳遞到Binder Driver層;
BINDER_RETURN_PROTOCOL :binder響應(yīng)碼,以”BR_“開頭,簡稱BR碼,用于從Binder Driver層傳遞到IPC層;
列舉一次完整的Binder通信過程:

BC_PROTOCOL:
| 請求碼 | 參數(shù)類型 | 作用 | 調(diào)用方法 |
|---|---|---|---|
| BC_TRANSACTION | binder_transaction_data | Client向Binder驅(qū)動發(fā)送請求數(shù)據(jù) | IPC.transact() |
| BC_REPLY | binder_transaction_data | Server向Binder驅(qū)動發(fā)送請求數(shù)據(jù) | IPC.sendReply() |
BR_PROTOCOL:
| 響應(yīng)碼 | 參數(shù)類型 | 作用 | 觸發(fā)時(shí)機(jī) |
|---|---|---|---|
| BR_TRANSACTION | binder_transaction_data | Binder驅(qū)動向Server端發(fā)送請求數(shù)據(jù) | 收到BINDER_WORK_TRANSACTION |
| BR_REPLY | binder_transaction_data | Binder驅(qū)動向Client端發(fā)送回復(fù)數(shù)據(jù) | 收到BINDER_WORK_TRANSACTION |
| BR_TRANSACTION_COMPLETE | 無參數(shù) | 對請求發(fā)送的成功反饋 | 收到BINDER_WORK_TRANSACTION_COMPLETE |
BC_TRANSACTION和BR_TRANSACTION過程是一個(gè)client請求server的完整事務(wù)過程
BC_REPLY和BR_REPLY過程是server回復(fù)client的完整事務(wù)過程。
binder_thread_write()根據(jù)不同的BC協(xié)議而執(zhí)行不同的流程。 其中BC_TRANSACTION和BC_REPLY協(xié)議,會進(jìn)入binder_transaction()過程。
BINDER_WORK_TRANSACTION和BINDER_WORK_TRANSACTION_COMPLETE都是binder_work類型。
注:控制協(xié)議和驅(qū)動協(xié)議不止這么點(diǎn),在此只是列舉了最常見的。
四、通信過程
按協(xié)議來看:

按數(shù)據(jù)轉(zhuǎn)換來看:

圖(左)說明:
- AMP.startService: 將數(shù)據(jù)封裝到Parcel類型;
- IPC.writeTransactionData:將數(shù)據(jù)封裝到binder_transaction_data結(jié)構(gòu)體;
- IPC.talkWithDriver:將數(shù)據(jù)進(jìn)一步封裝到binder_write_read結(jié)構(gòu)體;
再通過ioctl()寫入命令BINDER_WRITE_READ和binder_write_read結(jié)構(gòu)體到驅(qū)動層 - binder_transaction: 將發(fā)起端數(shù)據(jù)拷貝到接收端進(jìn)程的buffer結(jié)構(gòu)體;
圖(右)說明:
- binder_thread_read:根據(jù)binder_transaction結(jié)構(gòu)體和binder_buffer結(jié)構(gòu)體數(shù)據(jù)生成新的binder_transaction_data結(jié)構(gòu)體,寫入bwr的write_buffer,傳遞到用戶空間。
- IPC.executeCommand: 解析binder_transaction_data數(shù)據(jù),找到目標(biāo)BBinder并調(diào)用其transact()方法;
- AMN.onTransact: 解析Parcel數(shù)據(jù),然后調(diào)用目標(biāo)服務(wù)的目標(biāo)方法;
- AMS.startService: 層層封裝和拆分后,執(zhí)行真正的業(yè)務(wù)邏輯。
五、Binder內(nèi)存機(jī)制

虛擬進(jìn)程地址空間(vm_area_struct)和虛擬內(nèi)核地址空間(vm_struct)都映射到同一塊物理內(nèi)存空間。當(dāng)Client端與Server端發(fā)送數(shù)據(jù)時(shí),Client(作為數(shù)據(jù)發(fā)送端)先從自己的進(jìn)程空間把IPC通信數(shù)據(jù)copy_from_user拷貝到內(nèi)核空間,而Server端(作為數(shù)據(jù)接收端)與內(nèi)核共享數(shù)據(jù),不再需要拷貝數(shù)據(jù),而是通過內(nèi)存地址空間的偏移量,即可獲悉內(nèi)存地址,整個(gè)過程只發(fā)生一次內(nèi)存拷貝。一般地做法,需要Client端進(jìn)程空間拷貝到內(nèi)核空間,再由內(nèi)核空間拷貝到Server進(jìn)程空間,會發(fā)生兩次拷貝。
下面這圖是從Binder在進(jìn)程間數(shù)據(jù)通信的流程圖,從圖中更能明了Binder的內(nèi)存轉(zhuǎn)移關(guān)系。

所以總體來說,Binder驅(qū)動主要分兩塊:協(xié)議的通信 和 內(nèi)存空間處理 。
參考文章:
https://blog.csdn.net/qiyei2009/article/details/78996106
http://gityuan.com/2015/11/01/binder-driver/
http://gityuan.com/2015/11/02/binder-driver-2/