Binder 驅(qū)動和 ServiceManager 通信流程

前言

本文基于 linux 3.18 和 Android 9.0 版本源碼,涉及的源碼文件路徑為:

背景

整理 Android Binder IPC 知識點,記錄閱讀 Binder 源碼的理解

是什么

Binder IPC 四大模塊
  • 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)換功能
  • 本文主要記錄這兩者之間的通信流程

時序圖

Binder 驅(qū)動和 ServiceManager 通信時序圖
  • 系統(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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窟哺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子技肩,更是在濱河造成了極大的恐慌且轨,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異旋奢,居然都是意外死亡泳挥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門至朗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屉符,“玉大人,你說我怎么就攤上這事爽丹≈螅” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵粤蝎,是天一觀的道長。 經(jīng)常有香客問我袋马,道長初澎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任虑凛,我火速辦了婚禮碑宴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桑谍。我一直安慰自己延柠,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布锣披。 她就那樣靜靜地躺著贞间,像睡著了一般。 火紅的嫁衣襯著肌膚如雪雹仿。 梳的紋絲不亂的頭發(fā)上增热,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音胧辽,去河邊找鬼峻仇。 笑死,一個胖子當著我的面吹牛邑商,可吹牛的內(nèi)容都是我干的摄咆。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼人断,長吁一口氣:“原來是場噩夢啊……” “哼吭从!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起含鳞,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤影锈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸭廷,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡枣抱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辆床。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佳晶。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖讼载,靈堂內(nèi)的尸體忽然破棺而出轿秧,到底是詐尸還是另有隱情,我是刑警寧澤咨堤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布菇篡,位于F島的核電站,受9級特大地震影響一喘,放射性物質(zhì)發(fā)生泄漏驱还。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一凸克、第九天 我趴在偏房一處隱蔽的房頂上張望议蟆。 院中可真熱鬧,春花似錦萎战、人聲如沸咐容。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽戳粒。三九已至,卻和暖如春鸟雏,著一層夾襖步出監(jiān)牢的瞬間享郊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工孝鹊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炊琉,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓又活,卻偏偏與公主長得像苔咪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子柳骄,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容