此文只是自己學(xué)習(xí)記錄慧邮,很籠統(tǒng)调限,沒學(xué)那么深舟陆,如有錯誤還請指正
1. Android 多線程通信概述
Android 系統(tǒng)是基于 Linux 內(nèi)核的,Linux 已經(jīng)提供了管道耻矮、消息隊(duì)列秦躯、共享內(nèi)存和 Socket 等 IPC 機(jī)制。那為什么 Android 還要提供 Binder 來實(shí)現(xiàn) IPC 呢踱承?主要是基于性能、穩(wěn)定性和安全性幾方面的原因哨免。
性能
首先說說性能上的優(yōu)勢茎活。Socket 作為一款通用接口,其傳輸效率低琢唾,開銷大载荔,主要用在跨網(wǎng)絡(luò)的進(jìn)程間通信和本機(jī)上進(jìn)程間的低速通信。消息隊(duì)列和管道采用存儲-轉(zhuǎn)發(fā)方式采桃,即數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中懒熙,然后再從內(nèi)核緩存區(qū)拷貝到接收方緩存區(qū),至少有兩次拷貝過程普办。共享內(nèi)存雖然無需拷貝工扎,但控制復(fù)雜,難以使用泌豆。Binder 只需要一次數(shù)據(jù)拷貝定庵,性能上僅次于共享內(nèi)存蔬浙。
Binder優(yōu)勢匯總
Binder是怎么實(shí)現(xiàn)一次內(nèi)存拷貝的
Binder使用mmap技術(shù)實(shí)現(xiàn)了 接收進(jìn)程(用戶空間)緩存區(qū)到內(nèi)核空間緩存區(qū)的映射贞远,所以當(dāng)發(fā)送進(jìn)程通過系統(tǒng)調(diào)用將自己的數(shù)據(jù)copy到內(nèi)核空間時畴博,就相當(dāng)于直接copy到接收進(jìn)程的緩存中去,實(shí)現(xiàn)了一次copy蓝仲。
2. 多進(jìn)程通信的過程
Binder結(jié)構(gòu)下的多進(jìn)程通信少不了四個元素
- 內(nèi)核空間下的Binder驅(qū)動俱病,可以理解為Binder也是一個對象
- 守護(hù)進(jìn)程下的 ServerManager
- Client 進(jìn)程
- Server進(jìn)程
Binder 驅(qū)動
Binder 驅(qū)動就如同路由器一樣,是整個通信的核心袱结;驅(qū)動負(fù)責(zé)進(jìn)程之間 Binder 通信的建立,Binder 在進(jìn)程之間的傳遞,Binder 引用計數(shù)管理,數(shù)據(jù)包在進(jìn)程之間的傳遞和交互等一系列底層支持促王。
ServiceManager 與實(shí)名 Binder
ServiceManager 和 DNS 類似,作用是將字符形式的 Binder 名字轉(zhuǎn)化成 Client 中對該 Binder 的引用蝇狼,使得 Client 能夠通過 Binder 的名字獲得對 Binder 實(shí)體的引用阅畴。注冊了名字的 Binder 叫實(shí)名 Binder,就像網(wǎng)站一樣除了除了有 IP 地址意外還有自己的網(wǎng)址迅耘。Server 創(chuàng)建了 Binder冯事,并為它起一個字符形式,可讀易記得名字彰触,將這個 Binder 實(shí)體連同名字一起以數(shù)據(jù)包的形式通過 Binder 驅(qū)動發(fā)送給 ServiceManager ,通知 ServiceManager 注冊一個名為“張三”的 Binder,它位于某個 Server 中辉巡。驅(qū)動為這個穿越進(jìn)程邊界的 Binder 創(chuàng)建位于內(nèi)核中的實(shí)體節(jié)點(diǎn)以及 ServiceManager 對實(shí)體的引用,將名字以及新建的引用打包傳給 ServiceManager红氯。ServiceManger 收到數(shù)據(jù)后從中取出名字和引用填入查找表框咙。
細(xì)心的讀者可能會發(fā)現(xiàn)咕痛,ServierManager 是一個進(jìn)程,Server 是另一個進(jìn)程喇嘱,Server 向 ServiceManager 中注冊 Binder 必然涉及到進(jìn)程間通信茉贡。當(dāng)前實(shí)現(xiàn)進(jìn)程間通信又要用到進(jìn)程間通信,這就好像蛋可以孵出雞的前提卻是要先找只雞下蛋者铜!Binder 的實(shí)現(xiàn)比較巧妙腔丧,就是預(yù)先創(chuàng)造一只雞來下蛋。ServiceManager 和其他進(jìn)程同樣采用 Bidner 通信作烟,ServiceManager 是 Server 端愉粤,有自己的 Binder 實(shí)體,其他進(jìn)程都是 Client拿撩,需要通過這個 Binder 的引用來實(shí)現(xiàn) Binder 的注冊衣厘,查詢和獲取。ServiceManager 提供的 Binder 比較特殊压恒,它沒有名字也不需要注冊影暴。當(dāng)一個進(jìn)程使用 BINDERSETCONTEXT_MGR 命令將自己注冊成 ServiceManager 時 Binder 驅(qū)動會自動為它創(chuàng)建 Binder 實(shí)體(這就是那只預(yù)先造好的那只雞)。其次這個 Binder 實(shí)體的引用在所有 Client 中都固定為 0 而無需通過其它手段獲得探赫。也就是說型宙,一個 Server 想要向 ServiceManager 注冊自己的 Binder 就必須通過這個 0 號引用和 ServiceManager 的 Binder 通信。類比互聯(lián)網(wǎng)伦吠,0 號引用就好比是域名服務(wù)器的地址妆兑,你必須預(yù)先動態(tài)或者手工配置好。要注意的是毛仪,這里說的 Client 是相對于 ServiceManager 而言的搁嗓,一個進(jìn)程或者應(yīng)用程序可能是提供服務(wù)的 Server,但對于 ServiceManager 來說它仍然是個 Client箱靴。
Client 獲得實(shí)名 Binder 的引用
Server 向 ServiceManager 中注冊了 Binder 以后谱姓, Client 就能通過名字獲得 Binder 的引用了。Client 也利用保留的 0 號引用向 ServiceManager 請求訪問某個 Binder: 我申請訪問名字叫張三的 Binder 引用刨晴。ServiceManager 收到這個請求后從請求數(shù)據(jù)包中取出 Binder 名稱屉来,在查找表里找到對應(yīng)的條目,取出對應(yīng)的 Binder 引用作為回復(fù)發(fā)送給發(fā)起請求的 Client狈癞。從面向?qū)ο蟮慕嵌瓤辞芽浚琒erver 中的 Binder 實(shí)體現(xiàn)在有兩個引用:一個位于 ServiceManager 中,一個位于發(fā)起請求的 Client 中蝶桶。如果接下來有更多的 Client 請求該 Binder慨绳,系統(tǒng)中就會有更多的引用指向該 Binder ,就像 Java 中一個對象有多個引用一樣。
具體的通信過程如下
- 首先脐雪,一個進(jìn)程使用 BINDERSETCONTEXT_MGR 命令通過 Binder 驅(qū)動將自己注冊成為 ServiceManager厌小;
- Server 通過Binder驅(qū)動向 ServiceManager 中注冊 Binder(Server 中的 Binder 實(shí)體),表明可以對外提供服務(wù)战秋。驅(qū)動為這個 Binder 創(chuàng)建位于內(nèi)核中的實(shí)體節(jié)點(diǎn)以及 ServiceManager 對實(shí)體的引用璧亚,將名字以及新建的引用打包傳給 ServiceManager,ServiceManger 將其填入查找表脂信。
- Client 通過名字癣蟋,在 Binder 驅(qū)動的幫助下從 ServiceManager 中獲取到對 Binder 實(shí)體的引用,通過這個引用就能實(shí)現(xiàn)和 Server 進(jìn)程的通信狰闪。
現(xiàn)在我們可以對 Binder 做個更加全面的定義:
- 從進(jìn)程間通信的角度看疯搅,Binder 是一種進(jìn)程間通信的機(jī)制;
- 從 Server 進(jìn)程的角度看埋泵,Binder 指的是 Server 中的 Binder 實(shí)體對象幔欧;
- 從 Client 進(jìn)程的角度看,Binder 指的是對 Binder 代理對象丽声,是 Binder 實(shí)體對象的一個遠(yuǎn)程代理
- 從傳輸過程的角度看琐馆,Binder 是一個可以跨進(jìn)程傳輸?shù)膶ο螅籅inder 驅(qū)動會對這個跨越進(jìn)程邊界的對象對一點(diǎn)點(diǎn)特殊處理恒序,自動完成代理對象和本地對象之間的轉(zhuǎn)換。