了解了 Linux IPC 相關(guān)的概念和通信原理, 下面正式介紹下 Binder IPC 的原理.
(如有侵權(quán), 請(qǐng)聯(lián)系刪除)
1. 動(dòng)態(tài)內(nèi)核可加載模塊 && 內(nèi)存映射
正如上一章所說(shuō), 跨進(jìn)程通信是需要內(nèi)核空間做支持的. 傳統(tǒng)的 IPC 機(jī)制如 管道, Socket, 都是內(nèi)核的一部分, 因此通過(guò)內(nèi)核支持來(lái)實(shí)現(xiàn)進(jìn)程間通信自然是沒(méi)問(wèn)題的.
但是 Binder 并不是 Linux 系統(tǒng)內(nèi)核的一部分, 那怎么辦呢, 這得益于 Linux 的動(dòng)態(tài)內(nèi)核可加載模塊 (Loadable Kernel Module, LKM)的機(jī)制
1.1 動(dòng)態(tài)內(nèi)核可加載模塊 (Loadable Kernel Module, LKM)
動(dòng)態(tài)內(nèi)核可加載模塊 (Loadable Kernel Module, LKM)
?
模塊是具有獨(dú)立功能的程序, 它可以被單獨(dú)編譯, 但是不能獨(dú)立運(yùn)行. 它在運(yùn)行時(shí)被鏈接到內(nèi)核作為內(nèi)核的一部分運(yùn)行.
這樣 Android 系統(tǒng)就可以通過(guò)動(dòng)態(tài)添加一個(gè)內(nèi)核模塊運(yùn)行在內(nèi)核空間, 用戶(hù)進(jìn)程進(jìn)程之間通過(guò)這個(gè)內(nèi)核模塊作為橋梁來(lái)實(shí)現(xiàn)通信.
在 Android 中, 這個(gè)運(yùn)行在內(nèi)核空間, 負(fù)責(zé)各個(gè)用戶(hù)進(jìn)程通過(guò) Binder 實(shí)現(xiàn)通信的內(nèi)核模塊就叫做 Binder 驅(qū)動(dòng) (Binder Driver)
那么在 Android 系統(tǒng)中用戶(hù)進(jìn)程之間是如何通過(guò)這個(gè)內(nèi)核模塊 (Binder Driver)來(lái)實(shí)現(xiàn)通信的呢? 顯然不是和上一章的傳統(tǒng) IPC 通信一樣,進(jìn)行兩次 copy 了, 不然Binder 也不有在性能方面的優(yōu)勢(shì)了.
1.2 內(nèi)存映射
Binder IPC 機(jī)制中設(shè)計(jì)到的內(nèi)存映射通過(guò) mmap()
來(lái)實(shí)現(xiàn), mmap()
是操作系統(tǒng)中一種內(nèi)存映射的方法.
內(nèi)存映射
?
簡(jiǎn)單的說(shuō)就是將用戶(hù)空間的一塊內(nèi)存區(qū)域映射到內(nèi)核空間.
映射關(guān)系建立后, 用戶(hù)對(duì)這塊內(nèi)存區(qū)域的修改可以直接反應(yīng)到內(nèi)核空間.
反之,內(nèi)核空間對(duì)這段區(qū)域的修改也能直接反應(yīng)到用戶(hù)空間.
內(nèi)存映射能減少數(shù)據(jù) copy 的次數(shù), 實(shí)現(xiàn)用戶(hù)空間和內(nèi)核空間的高效互動(dòng). 兩個(gè)空間各自的修改也能直接反應(yīng)在映射的內(nèi)存區(qū)域, 從而被對(duì)方空間及時(shí)感知. 也正因?yàn)槿绱? 內(nèi)存映射能夠提供對(duì)進(jìn)程間通信的支持.
2. Binder IPC 實(shí)現(xiàn)原理
Binder IPC 正是基于內(nèi)存映射(mmap()
) 來(lái)實(shí)現(xiàn)的, 但是mmap()
通常是用在有物理介質(zhì)的文件系統(tǒng)上的.
比如進(jìn)程中的用戶(hù)區(qū)域是不能直接和物理設(shè)備打交道的, 如果想要把磁盤(pán)上的數(shù)據(jù)讀取到進(jìn)程的用戶(hù)區(qū)域, 需要兩次 copy (磁盤(pán) -> 內(nèi)核空間 -> 用戶(hù)空間). 通常在這種場(chǎng)景下 mmap()
就能發(fā)揮作用, 通過(guò)在物理介質(zhì)和用戶(hù)空間之間建立映射, 減少數(shù)據(jù)的 copy 次數(shù), 用內(nèi)存讀寫(xiě)代替 I/O 讀寫(xiě), 提高文件讀取效率.
而 Binder 并不存在物理介質(zhì), 因此 Binder 驅(qū)動(dòng)使用 mmap()
并不是為了在物理介質(zhì)和用戶(hù)空間之間映射, 而是用來(lái)在內(nèi)核空間創(chuàng)建數(shù)據(jù)接收的緩存空間.
一次完整的 Binder IPC 通信過(guò)程通常是這樣:
- 首先 Binder 驅(qū)動(dòng)在內(nèi)核空間創(chuàng)建一個(gè)數(shù)據(jù)接收緩存區(qū)
- 接著在內(nèi)核空間開(kāi)辟一塊內(nèi)核緩存區(qū).
- 建立內(nèi)核緩存區(qū) 和 內(nèi)核中數(shù)據(jù)接收緩存區(qū) 之間的映射關(guān)系.
- 建立內(nèi)核中數(shù)據(jù)接收緩存區(qū) 與 數(shù)據(jù)接收進(jìn)程用戶(hù)空間 的地址的映射關(guān)系
- 發(fā)送方進(jìn)程通過(guò)系統(tǒng)調(diào)用
copy_from_user()
將數(shù)據(jù) copy 到內(nèi)核中的內(nèi)核緩存區(qū), 由于內(nèi)核緩存區(qū) 與 內(nèi)核數(shù)據(jù)接收緩存區(qū) 存在映射關(guān)系, 而內(nèi)核數(shù)據(jù)接收緩存區(qū) 又與 數(shù)據(jù)接收進(jìn)程用戶(hù)空間 存在映射關(guān)系, 所以相當(dāng)于 內(nèi)核緩存區(qū) 與 數(shù)據(jù)接收進(jìn)程用戶(hù)空間存在映射關(guān)系. 因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了數(shù)據(jù)接收進(jìn)程的用戶(hù)空間.
這樣就完成了一次進(jìn)程間通信
如下圖:
3. Binder 通信模型
介紹完 Binder IPC 的底層通信原理, 接下來(lái)我們看看實(shí)現(xiàn)層面是如何設(shè)計(jì)的
一次完成的進(jìn)程間通信必然至少包含兩個(gè)進(jìn)程, 通常我們稱(chēng)通信的雙方分別為客戶(hù)端進(jìn)程(Client) 和服務(wù)端進(jìn)程(Server), 由于進(jìn)程隔離機(jī)制的存在, 通信雙方必然需要借助 Binder 來(lái)實(shí)現(xiàn).
3.1 Client/Server/ServiceManager/Binder驅(qū)動(dòng)
BInder 是基于 C/S 架構(gòu). 是由一些列組件組成. 包括 Client, Server, ServiceManager, Binder 驅(qū)動(dòng).
- Client, Server, ServiceManager 運(yùn)行在用戶(hù)空間
- Binder 驅(qū)動(dòng)運(yùn)行在內(nèi)核空間.
- ServiceManager, Binder 驅(qū)動(dòng)由系統(tǒng)提供.
- Client, Service 由應(yīng)用程序來(lái)實(shí)現(xiàn)
Client, Server, ServiceManager 都是通過(guò)系統(tǒng)調(diào)用
open, mmap,
和ioctl
來(lái)訪問(wèn)設(shè)備文件/dev/binder
, 從而實(shí)現(xiàn)與 Binder 驅(qū)動(dòng)的交互來(lái)間接的實(shí)現(xiàn)跨進(jìn)程通信.
3.1.1 Binder 驅(qū)動(dòng)
Binder 驅(qū)動(dòng)就如如同路由器一樣, 是整個(gè)通信的核心. 驅(qū)動(dòng)負(fù)責(zé)進(jìn)程之間 Binder 通信的建立 / 傳遞, Binder 引用計(jì)數(shù)管理, 數(shù)據(jù)包在進(jìn)程之間的傳遞和交互等一系列底層支持.
3.1.2 ServiceManager 與 實(shí)名 Binder
ServiceManager 作用是將字符形式的 Binder 名字轉(zhuǎn)化成 Client 中對(duì)該 Binder 的引用, 使得 Client 能夠通過(guò) Binder 的名字獲得對(duì) Binder 實(shí)體的引用.
注冊(cè)了名字的 Binder 叫實(shí)名 Binder, 就像網(wǎng)站一樣除了 IP 地址以外還有自己的網(wǎng)址.
Server 創(chuàng)建了 Binder, 并為它起一個(gè)字符形式, 可讀易記的名字, 將這個(gè) BInder 實(shí)體連同名字一起以數(shù)據(jù)包的形式通過(guò) Binder 驅(qū)動(dòng) 發(fā)送給 ServiceManager, 通知 ServiceManager 注冊(cè)一個(gè)名字為 "張三"的 Binder, 它位于某個(gè) Server 中, 驅(qū)動(dòng)為這個(gè)穿越進(jìn)程邊界的 BInder 創(chuàng)建位于內(nèi)核中的實(shí)體節(jié)點(diǎn)以及 ServiceManager 對(duì)實(shí)體的引用, 將名字以及新建的引用打包傳給 ServiceManager, ServiceManager 收到數(shù)據(jù)后從中取出名字和引用填入查找表.
ServiceManager 是一個(gè)進(jìn)程, Server 又是一個(gè)另外的進(jìn)程, Server 向 ServiceManager 中注冊(cè) BInder 必然涉及到進(jìn)程間通信. 當(dāng)實(shí)現(xiàn)進(jìn)程間通信又要用到進(jìn)程間通信, 這就好像蛋可以孵出雞的前提確實(shí)要先找只雞下蛋! Binder 的實(shí)現(xiàn)比較巧妙, 就是預(yù)先創(chuàng)造一只雞來(lái)下蛋. ServiceManager 和其他進(jìn)程同樣采用 Binder 通信, ServiceManager 是 Server 端, 有自己的 Binder 實(shí)體, 其他進(jìn)程都是 Client, 需要通過(guò)這個(gè) Binder 的引用來(lái)實(shí)現(xiàn) Binder 的注冊(cè), 查詢(xún)和獲取. ServiceManager 提供的 Binder 比較特殊, 它沒(méi)有名字也不需要注冊(cè). 當(dāng)一個(gè)進(jìn)程使用 BINDERSETCONTEXT_MGR 命令將自己注冊(cè)成 ServiceManager 時(shí) Binder 驅(qū)動(dòng)會(huì)自動(dòng)為它創(chuàng)建 Binder 實(shí)體(這就是那只預(yù)先造好的那只雞). 其實(shí)這個(gè) Binder 實(shí)體的引用在所有 Client 中都固定為 0 , 而無(wú)需通過(guò)其他手段獲得. 也就是說(shuō), 一個(gè) Server 想要向 ServiceManager 注冊(cè)自己的 Binder 就必須通過(guò)這個(gè) 0 號(hào)引用和 ServiceManager 的 Binder 通信. 這里說(shuō)的 Client 是相對(duì)于 ServiceManager 而言的, 一個(gè)進(jìn)程或者應(yīng)用程序可能是提供服務(wù)的 Server, 但是對(duì)于 ServiceManager 來(lái)說(shuō)它仍然是個(gè) Client.
Server 向 ServiceManager 中注冊(cè)了 Binder 以后, Client 就能通過(guò)名字獲得 Binder 的引用. Client 也利用保留的 0 號(hào)引用向 ServiceManager 請(qǐng)求訪問(wèn)某個(gè) Binder. 比如,Client 申請(qǐng)?jiān)L問(wèn)名字叫"張三"的 Binder 引用. ServiceManager 收到這個(gè)請(qǐng)求后從請(qǐng)求數(shù)據(jù)包中取出 Binder 名稱(chēng), 在查找表里找到對(duì)應(yīng)的條目, 取出對(duì)應(yīng)的 Binder 引用, 作為回復(fù)發(fā)送給發(fā)起請(qǐng)求的 Client. 從面相對(duì)象的角度看, Server 中的 Binder 實(shí)體現(xiàn)在有兩個(gè)引用: 一個(gè)位于 ServiceManager 中, 一個(gè)位于發(fā)起請(qǐng)求的 Client 中. 如果后面會(huì)有更多的 Client 請(qǐng)求該 Binder, 系統(tǒng)中就會(huì)有更多的引用指向這個(gè) Binder, 就像 Java 中一個(gè)對(duì)象有多個(gè)引用一樣.
4. Binder 通信中的代理模式
我們已經(jīng)解釋清楚 Client, Server 借助 Binder 驅(qū)動(dòng)完成跨進(jìn)程通信的實(shí)現(xiàn)機(jī)制了, 但是還有個(gè)問(wèn)題需要弄清楚, 比如 A 進(jìn)程想要 B 進(jìn)程中的某個(gè)對(duì)象(object) 是如何實(shí)現(xiàn)的呢, 畢竟它們屬于不同的進(jìn)程, A 進(jìn)程沒(méi)辦法直接使用 B 進(jìn)程中的 object.
前面我們說(shuō)過(guò)跨進(jìn)程通信的過(guò)程都有 Binder 驅(qū)動(dòng)的參與, 因此在數(shù)據(jù)流經(jīng) Binder 驅(qū)動(dòng)的時(shí)候 Binder 驅(qū)動(dòng)會(huì)對(duì)數(shù)據(jù)做一層轉(zhuǎn)換.
我們?cè)?Client端掏击,向 ServiceManager 獲取具體的 Server 端的 Binder 引用的時(shí)候突琳,會(huì)首先進(jìn)過(guò) Binder 驅(qū)動(dòng)索守,Binder 驅(qū)動(dòng)它并不會(huì)把真正的 Server 的 Binder 引用返回給 Client 端桌硫,而是返回一個(gè)代理的 java 對(duì)象,該對(duì)象具有跟 Server 端的 Binder 引用相同的方法簽名饵逐,這個(gè)對(duì)象為 ProxyObject另玖,他具有跟 Server 的 Binder 實(shí)例一樣的方法袱贮,只是這些方法并沒(méi)有 Server 端的能力,這些方法只需要把請(qǐng)求參數(shù)交給 Binder 驅(qū)動(dòng)即可. 對(duì)于 Client 端來(lái)說(shuō)和直接調(diào)用 Server 中的方法是一樣的.
5. Binder 通信過(guò)程
了解了上面之后, 我們大致可以推算出 Binder 的通信過(guò)程
1. 注冊(cè) ServiceManager
一個(gè)進(jìn)程使用 BINDERSETCONTEXT_MGR 命令通過(guò) Binder 驅(qū)動(dòng)將自己注冊(cè)成為 ServiceManager.
2. 注冊(cè) Server
Server 通過(guò) Binder 驅(qū)動(dòng)向 ServiceManager 中注冊(cè) Binder (Server 中 Binder 實(shí)體), 表明可以對(duì)外提供服務(wù).
Binder 驅(qū)動(dòng)為這個(gè) Binder 創(chuàng)建位于內(nèi)核中的實(shí)體節(jié)點(diǎn)以及 ServiceManager 對(duì)實(shí)體的引用, 然后將名字及新建的引用打包傳給 ServiceManager, 最后 ServiceManager 將其填入到查找表.
3. Client 獲取 Server 的 Binder 引用
Client 請(qǐng)求獲得 Server 的 Binder 引用, Binder 驅(qū)動(dòng)在接收到 Client 的請(qǐng)求的時(shí)候, 就去 ServiceManager 中查詢(xún), 查詢(xún)到后會(huì)創(chuàng)建一個(gè) Server 端 Binder 對(duì)象的 ProxyObject (Binder 的代理對(duì)象), 并將該 ProxyObject 返回給 Client.
4. Client 與 Server 通信
Client 收到 Binder 驅(qū)動(dòng)返回的 ProxyObject 后, 通過(guò) ProxyObject 調(diào)用其中的方法. ProxyObject 的方法再去調(diào)用 Binder 驅(qū)動(dòng), Binder 驅(qū)動(dòng)會(huì)去查詢(xún)自己維護(hù)的表單, 發(fā)現(xiàn)這是某個(gè) Server 端 Binder 的代理對(duì)象, 就會(huì)通知 Server 端調(diào)用具體的方法,(會(huì)把 Client 端調(diào)用 ProxyObject 中方法的參數(shù)傳過(guò)去. ), 并要求 Server 把結(jié)果返回給自己. 當(dāng) Binder 驅(qū)動(dòng)拿到結(jié)果后, 就會(huì)轉(zhuǎn)發(fā)給 Client 端.
6. Binder 的完整定義
- 從進(jìn)程間通信的角度看, Binder 是一種進(jìn)程間通信的機(jī)制
- 從 Server 進(jìn)程的角度看, Binder 指的是 Server 中的 Binder 實(shí)體對(duì)象.
- 從 Client 進(jìn)程的角度看, Binder 指的是對(duì) Binder 代理對(duì)象, 是 Binder 實(shí)體對(duì)象的一個(gè)遠(yuǎn)程代理.
- 從傳輸過(guò)程的角度看, Binder 是一個(gè)可以跨進(jìn)程通信的對(duì)象. Binder 驅(qū)動(dòng)會(huì)對(duì)這個(gè)跨越進(jìn)程邊界的對(duì)象