前言
本文基于 linux 3.18 和 Android 9.0 版本源碼,涉及的源碼文件路徑為:
- Binder 驅(qū)動
http://androidxref.com/kernel_3.18/xref/drivers/staging/android/binder.c - ServiceManager
背景
整理 Android Binder IPC 知識點,記錄閱讀 Binder 源碼的理解
是什么
- Binder 驅(qū)動是 Binder IPC 架構(gòu)中內(nèi)核層程序,負責(zé)處理應(yīng)用層發(fā)送過來的請求,完成多進程間傳輸數(shù)據(jù)功能
- ServiceManager 是應(yīng)用層程序,本身是一個 Binder Server,負責(zé)記錄和查找系統(tǒng)中其他 Binder Server敦间,完成 Binder 標識符到 Binder 實體的轉(zhuǎn)換功能
- 本文主要記錄這兩者之間的通信流程
時序圖
- 系統(tǒng)啟動時執(zhí)行 device_initcall 函數(shù)完成驅(qū)動初始化
static int __init binder_init(void)
{
// 我運行在內(nèi)核啟動時加載驅(qū)動的線程
...
ret = misc_register(&binder_miscdev);
...
}
- Binder 驅(qū)動向系統(tǒng)注冊名字為 “binder” 的 misc 設(shè)備。注冊的目的是為了之后系統(tǒng)中可以查找到該驅(qū)動束铭,否則打開 Binder 驅(qū)動失敗
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
// 注冊的設(shè)備名稱廓块。接下來通過 open 系統(tǒng)調(diào)用可以查找到是我
.name = "binder",
.fops = &binder_fops
};
- 接下來系統(tǒng)執(zhí)行 ServiceManager 啟動的 main 函數(shù)
int main(int argc, char** argv)
{
...
if (argc > 1) {
driver = argv[1];
} else {
// 驅(qū)動名稱。對應(yīng) binder driver 注冊的名稱
driver = "/dev/binder";
}
// 打開 binder driver纯露,并映射虛擬內(nèi)存 128K
bs = binder_open(driver, 128*1024);
...
}
- ServiceManager 通過 open 函數(shù)打開 binder 設(shè)備
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
...
//系統(tǒng)調(diào)用 open剿骨,跳轉(zhuǎn)到內(nèi)核代碼段執(zhí)行
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
...
}
- ServiceManager 通過 ioctl 函數(shù)發(fā)送命令 BINDER_VERSION 讀取內(nèi)核 Binder 驅(qū)動的版本號。如果應(yīng)用層的 Binder 版本號和驅(qū)動層的 Binder 版本號不一致埠褪,則無法啟動 ServiceManager 程序
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
...
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
fprintf(stderr,
"binder: kernel driver version (%d) differs from user space version (%d)\n",
vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
goto fail_open;
}
...
}
- ServiceManager 通過 mmap 函數(shù)請求映射 128 KB 虛擬內(nèi)存浓利,目的是讓 Binder 驅(qū)動和當前進程的虛擬內(nèi)存映射到共享的物理頁面。這樣其他進程向內(nèi)核空間地址寫入數(shù)據(jù)之后钞速,ServiceManger 用戶空間也可以讀取到相應(yīng)的數(shù)據(jù)贷掖,完成通信
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
...
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
if (bs->mapped == MAP_FAILED) {
fprintf(stderr,"binder: cannot map device (%s)\n",
strerror(errno));
goto fail_map;
}
...
}
- ServiceManager 通過 ioctl 系統(tǒng)調(diào)用發(fā)送 BINDER_SET_CTX_MANAGER 命令給 Binder 驅(qū)動,目的是在內(nèi)核地址空間記錄 ServiceManger 這個 Binder Server渴语,這樣接下來各個 Binder Client 可以從 Binder 驅(qū)動拿到 ServiceManager 這個 Binder Server
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
- SM 進入循環(huán)之前通知 BD 我要進入循環(huán)了
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
// binder 調(diào)用方和 binder driver 調(diào)用參數(shù)結(jié)構(gòu)體
struct binder_write_read bwr;
// 讀取緩沖區(qū)大小 32 * 4 = 128 字節(jié)
uint32_t readbuf[32];
// 寫入緩沖區(qū)大小為 0 字節(jié)苹威,說明調(diào)用方不需要寫入數(shù)據(jù)
bwr.write_size = 0;
// 寫入緩沖區(qū)已經(jīng)消費的字節(jié)長度為 0,說明為消費數(shù)據(jù)
bwr.write_consumed = 0;
// 寫入緩沖區(qū)為空
bwr.write_buffer = 0;
// 讀取緩沖區(qū)前 4 個字節(jié)寫入命令字 BC_ENTER_LOOPER驾凶,通知 binder driver 我要進入
// 循環(huán)事件處理了
readbuf[0] = BC_ENTER_LOOPER;
// 向 binder driver 寫入命令字
binder_write(bs, readbuf, sizeof(uint32_t));
...
}
- 最后 ServiceManager 進入一個事件驅(qū)動循環(huán)牙甫,循環(huán)通過 ioctl 系統(tǒng)調(diào)用讀取 Binder Driver 寫入的來自其他 Binder Client 發(fā)送的請求
void binder_loop(struct binder_state *bs, binder_handler func)
{
...
for (;;) {
// 讀取緩沖區(qū)大小 128 字節(jié)
bwr.read_size = sizeof(readbuf);
// 可寫入偏移量為 0
bwr.read_consumed = 0;
// 緩沖區(qū)地址/指針
bwr.read_buffer = (uintptr_t) readbuf;
// 調(diào)用 ioctl 系統(tǒng)調(diào)用進入內(nèi)核代碼讀取內(nèi)核 binder driver 收到的命令
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
...
// 解析讀取到的命令
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
...
}
}
- ServiceManager 讀取請求、解析請求调违、處理請求
- ServiceManager 通過 ioctl 系統(tǒng)調(diào)用發(fā)送響應(yīng)給 Binder Driver