1 什么是Binder?
- 從IPC角度來(lái)說(shuō), Binder 是 Android 中特有的一種跨進(jìn)程的通信方式
- Binder 可以理解成一種虛擬的物理設(shè)備,設(shè)備驅(qū)動(dòng)是/dev/binder
- 從 Android Framework 角度來(lái)說(shuō),Binder 是 ServiceManager 連接各種 Manager(ActivityManager变秦、WindowManager等) 和 ManagerService 的橋梁
- Android系統(tǒng)基本上可以看作是一個(gè)基于Binder通信的C/S架構(gòu)
Binder 形象類比圖
通常我們?cè)L問(wèn)一個(gè)網(wǎng)頁(yè)的步驟是這樣的:首先在瀏覽器輸入一個(gè)地址烟勋,如 www.google.com 然后按下回車鍵。但是并沒(méi)有辦法通過(guò)域名地址直接找到我們要訪問(wèn)的服務(wù)器稿壁,因此需要首先訪問(wèn) DNS 域名服務(wù)器,域名服務(wù)器中保存了 www.google.com 對(duì)應(yīng)的 ip 地址 10.249.23.13鞋仍,然后通過(guò)這個(gè) ip 地址才能放到到 www.google.com 對(duì)應(yīng)的服務(wù)器常摧。
Client、Server、ServiceManager落午、Binder 驅(qū)動(dòng)這幾個(gè)組件在通信過(guò)程中扮演的角色就如同互聯(lián)網(wǎng)中服務(wù)器(Server)谎懦、客戶端(Client)、DNS域名服務(wù)器(ServiceManager)以及路由器(Binder 驅(qū)動(dòng))之間的關(guān)系溃斋。
Binder 驅(qū)動(dòng)
Binder 驅(qū)動(dòng)就如同路由器一樣界拦,是整個(gè)通信的核心;驅(qū)動(dòng)負(fù)責(zé)進(jìn)程之間 Binder 通信的建立梗劫,Binder 在進(jìn)程之間的傳遞享甸,Binder 引用計(jì)數(shù)管理,數(shù)據(jù)包在進(jìn)程之間的傳遞和交互等一系列底層支持梳侨。
ServiceManager 與實(shí)名 Binder
ServiceManager 和 DNS 類似蛉威,作用是將字符形式的 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谈秫。ServiceManger 收到數(shù)據(jù)后從中取出名字和引用填入查找表。
ServierManager 是一個(gè)進(jìn)程拴签,Server 是另一個(gè)進(jìn)程孝常,Server 向 ServiceManager 中注冊(cè) Binder 必然涉及到進(jìn)程間通信旗们。當(dāng)前實(shí)現(xiàn)進(jìn)程間通信又要用到進(jìn)程間通信蚓哩,這就好像蛋可以孵出雞的前提卻是要先找只雞下蛋!Binder 的實(shí)現(xiàn)比較巧妙上渴,就是預(yù)先創(chuàng)造一只雞來(lái)下蛋岸梨。ServiceManager 和其他進(jìn)程同樣采用 Bidner 通信,ServiceManager 是 Server 端稠氮,有自己的 Binder 實(shí)體曹阔,其他進(jìn)程都是 Client,需要通過(guò)這個(gè) Binder 的引用來(lái)實(shí)現(xiàn) Binder 的注冊(cè)隔披,查詢和獲取赃份。ServiceManager 提供的 Binder 比較特殊,它沒(méi)有名字也不需要注冊(cè)。當(dāng)一個(gè)進(jìn)程使用 BINDER_SET_CONTEXT_MGR 命令將自己注冊(cè)成 ServiceManager 時(shí) Binder 驅(qū)動(dòng)會(huì)自動(dòng)為它創(chuàng)建 Binder 實(shí)體(這就是那只預(yù)先造好的那只雞)抓韩。其次這個(gè) Binder 實(shí)體的引用在所有 Client 中都固定為 0 而無(wú)需通過(guò)其它手段獲得纠永。也就是說(shuō),一個(gè) Server 想要向 ServiceManager 注冊(cè)自己的 Binder 就必須通過(guò)這個(gè) 0 號(hào)引用和 ServiceManager 的 Binder 通信谒拴。類比互聯(lián)網(wǎng)尝江,0 號(hào)引用就好比是域名服務(wù)器的地址,你必須預(yù)先動(dòng)態(tài)或者手工配置好英上。要注意的是炭序,這里說(shuō)的 Client 是相對(duì)于 ServiceManager 而言的,一個(gè)進(jìn)程或者應(yīng)用程序可能是提供服務(wù)的 Server苍日,但對(duì)于 ServiceManager 來(lái)說(shuō)它仍然是個(gè) Client惭聂。
Client 獲得實(shí)名 Binder 的引用
Server 向 ServiceManager 中注冊(cè)了 Binder 以后, Client 就能通過(guò)名字獲得 Binder 的引用了相恃。Client 也利用保留的 0 號(hào)引用向 ServiceManager 請(qǐng)求訪問(wèn)某個(gè) Binder: 我申請(qǐng)?jiān)L問(wèn)名字叫張三的 Binder 引用彼妻。ServiceManager 收到這個(gè)請(qǐng)求后從請(qǐng)求數(shù)據(jù)包中取出 Binder 名稱,在查找表里找到對(duì)應(yīng)的條目豆茫,取出對(duì)應(yīng)的 Binder 引用作為回復(fù)發(fā)送給發(fā)起請(qǐng)求的 Client侨歉。從面向?qū)ο蟮慕嵌瓤矗琒erver 中的 Binder 實(shí)體現(xiàn)在有兩個(gè)引用:一個(gè)位于 ServiceManager 中揩魂,一個(gè)位于發(fā)起請(qǐng)求的 Client 中幽邓。如果接下來(lái)有更多的 Client 請(qǐng)求該 Binder,系統(tǒng)中就會(huì)有更多的引用指向該 Binder 火脉,就像 Java 中一個(gè)對(duì)象有多個(gè)引用一樣牵舵。
2 Binder由來(lái)
Android 系統(tǒng)是基于 Linux 內(nèi)核的,Linux 已經(jīng)提供了管道倦挂、消息隊(duì)列畸颅、共享內(nèi)存和 Socket 等 IPC 機(jī)制。那為什么 Android 還要提供 Binder 來(lái)實(shí)現(xiàn) IPC 呢方援?主要是基于性能没炒、穩(wěn)定性和安全性幾方面的原因。
2.1 性能
- Socket 作為一款通用接口犯戏,其傳輸效率低步绸,開銷大饲漾,主要用在跨網(wǎng)絡(luò)的進(jìn)程間通信和本機(jī)上進(jìn)程間的低速通信泊碑。
- 消息隊(duì)列和管道采用存儲(chǔ)-轉(zhuǎn)發(fā)方式你弦,即數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中,然后再?gòu)膬?nèi)核緩存區(qū)拷貝到接收方緩存區(qū)呀非,至少有兩次拷貝過(guò)程坚俗。
- 共享內(nèi)存雖然無(wú)需拷貝,但控制復(fù)雜,難以使用猖败。
- Binder 只需要一次數(shù)據(jù)拷貝形耗,性能上僅次于共享內(nèi)存。
2.2 穩(wěn)定性
- Binder 基于 C/S 架構(gòu)辙浑,客戶端(Client)有什么需求就丟給服務(wù)端(Server)去完成激涤,架構(gòu)清晰、職責(zé)明確又相互獨(dú)立判呕,自然穩(wěn)定性更好倦踢。
- 共享內(nèi)存雖然無(wú)需拷貝,但是控制負(fù)責(zé)侠草,難以使用辱挥。
2.3 安全性
Android 作為一個(gè)開放性的平臺(tái),其上運(yùn)行這海量的應(yīng)用程序边涕,其安全性是不言而喻的晤碘。
傳統(tǒng)的 IPC 沒(méi)有任何安全措施,完全依賴上層協(xié)議來(lái)確保功蜓。
- 傳統(tǒng)的 IPC 接收方無(wú)法獲得對(duì)方可靠的進(jìn)程用戶ID/進(jìn)程ID(UID/PID)园爷,從而無(wú)法鑒別對(duì)方身份。Android 為每個(gè)安裝好的 APP 分配了自己的 UID式撼,故而進(jìn)程的 UID 是鑒別進(jìn)程身份的重要標(biāo)志童社。
- 傳統(tǒng)的 IPC 只能由用戶在數(shù)據(jù)包中填入 UID/PID,但這樣不可靠著隆,容易被惡意程序利用扰楼。可靠的身份標(biāo)識(shí)只有由 IPC 機(jī)制在內(nèi)核中添加美浦。
- 傳統(tǒng)的 IPC 訪問(wèn)接入點(diǎn)是開放的弦赖,只要知道這些接入點(diǎn)的程序都可以和對(duì)端建立連接,不管怎樣都無(wú)法阻止惡意程序通過(guò)猜測(cè)接收方地址獲得連接浦辨。
Binder 既支持實(shí)名 Binder蹬竖,又支持匿名 Binder,安全性高荤牍。
各種IPC方式數(shù)據(jù)拷貝次數(shù)
IPC | 數(shù)據(jù)拷貝次數(shù) |
---|---|
共享內(nèi)存 | 0 |
Binder | 1 |
Socket | 2 |
管道 | 2 |
消息隊(duì)列 | 2 |
基于上述原因案腺,Android 需要建立一套新的 IPC 機(jī)制來(lái)滿足系統(tǒng)對(duì)穩(wěn)定性庆冕、傳輸性能和安全性方面的要求康吵,這套新的 IPC 機(jī)制就是 Binder。
3 Linux 傳統(tǒng)的 IPC 原理
為了更好的理解 Binder 通信原理访递,我們先來(lái)了解下Linux 傳統(tǒng)的進(jìn)程間通信原理
我們先來(lái)了解下 Liunx 中跨進(jìn)程通信涉及到的一些基本概念
3.1 用戶空間/內(nèi)核空間
Kernel space 是 Linux 內(nèi)核的運(yùn)行空間晦嵌,User space 是用戶程序的運(yùn)行空間。 為了安全,它們是隔離的惭载,即使用戶的程序崩潰了旱函,內(nèi)核也不受影響。
Kernel space 可以執(zhí)行任意命令描滔,調(diào)用系統(tǒng)的一切資源棒妨; User space 只能執(zhí)行簡(jiǎn)單的運(yùn)算,不能直接調(diào)用系統(tǒng)資源含长,必須通過(guò)系統(tǒng)接口(又稱 system call)調(diào)用系統(tǒng)資源券腔。
3.2 系統(tǒng)調(diào)用/內(nèi)核態(tài)/用戶態(tài)
系統(tǒng)調(diào)用:用戶空間訪問(wèn)內(nèi)核空間的唯一方式就是系統(tǒng)調(diào)用;通過(guò)這個(gè)統(tǒng)一入口接口拘泞,所有的資源訪問(wèn)都是在內(nèi)核的控制下執(zhí)行纷纫,以免導(dǎo)致對(duì)用戶程序?qū)ο到y(tǒng)資源的越權(quán)訪問(wèn),從而保障了系統(tǒng)的安全和穩(wěn)定陪腌。
Linux 使用兩級(jí)保護(hù)機(jī)制:0 級(jí)供系統(tǒng)內(nèi)核使用辱魁,3 級(jí)供用戶程序使用。
內(nèi)核態(tài):當(dāng)一個(gè)任務(wù)(進(jìn)程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時(shí)诗鸭,我們就稱進(jìn)程處于內(nèi)核運(yùn)行態(tài)(或簡(jiǎn)稱為內(nèi)核態(tài))此時(shí)處理器處于特權(quán)級(jí)最高的(0級(jí))內(nèi)核代碼中執(zhí)行染簇。
用戶態(tài):當(dāng)進(jìn)程在執(zhí)行用戶自己的代碼時(shí),則稱其處于用戶運(yùn)行態(tài)(用戶態(tài))强岸。即此時(shí)處理器在特權(quán)級(jí)最低的(3級(jí))用戶代碼中運(yùn)行剖笙。
系統(tǒng)調(diào)用主要通過(guò)如下兩個(gè)函數(shù)來(lái)實(shí)現(xiàn)
copy_from_user() //將數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間
copy_to_user() //將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間
3.3 進(jìn)程隔離
簡(jiǎn)單的說(shuō)就是操作系統(tǒng)中,進(jìn)程與進(jìn)程間內(nèi)存是不共享的请唱。兩個(gè)進(jìn)程就像兩個(gè)平行的世界弥咪,A 進(jìn)程沒(méi)法直接訪問(wèn) B 進(jìn)程的數(shù)據(jù),這就是進(jìn)程隔離的通俗解釋十绑。A 進(jìn)程和 B 進(jìn)程之間要進(jìn)行數(shù)據(jù)交互就得采用特殊的通信機(jī)制:進(jìn)程間通信(IPC)聚至。
3.4 Linux 傳統(tǒng) IPC 通信原理
理解了上面的幾個(gè)概念,我們?cè)賮?lái)看看傳統(tǒng)的 IPC 方式中本橙,進(jìn)程之間是如何實(shí)現(xiàn)通信的扳躬。
通常的做法是消息發(fā)送方將要發(fā)送的數(shù)據(jù)存放在內(nèi)存緩存區(qū)中,通過(guò)系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài)甚亭。然后內(nèi)核程序在內(nèi)核空間分配內(nèi)存贷币,開辟一塊內(nèi)核緩存區(qū),調(diào)用 copy_from_user() 函數(shù)將數(shù)據(jù)從用戶空間的內(nèi)存緩存區(qū)拷貝到內(nèi)核空間的內(nèi)核緩存區(qū)中亏狰。同樣的役纹,接收方進(jìn)程在接收數(shù)據(jù)時(shí)在自己的用戶空間開辟一塊內(nèi)存緩存區(qū),然后內(nèi)核程序調(diào)用 copy_to_user() 函數(shù)將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到接收進(jìn)程的內(nèi)存緩存區(qū)暇唾。這樣數(shù)據(jù)發(fā)送方進(jìn)程和數(shù)據(jù)接收方進(jìn)程就完成了一次數(shù)據(jù)傳輸促脉,我們稱完成了一次進(jìn)程間通信辰斋。
這種傳統(tǒng)的 IPC 通信方式有兩個(gè)問(wèn)題:
- 一次數(shù)據(jù)傳遞需要經(jīng)歷:內(nèi)存緩存區(qū) --> 內(nèi)核緩存區(qū) --> 內(nèi)存緩存區(qū),需要 2 次數(shù)據(jù)拷貝瘸味,性能低宫仗。
- 接收數(shù)據(jù)的緩存區(qū)由數(shù)據(jù)接收進(jìn)程提供,但是接收進(jìn)程并不知道需要多大的空間來(lái)存放將要傳遞過(guò)來(lái)的數(shù)據(jù)旁仿,因此只能開辟盡可能大的內(nèi)存空間或者先調(diào)用 API 接收消息頭來(lái)獲取消息體的大小藕夫,這兩種做法不是浪費(fèi)空間就是浪費(fèi)時(shí)間。
4 Binder架構(gòu)
- Binder 通信采用 C/S 架構(gòu)枯冈,從組件視角來(lái)說(shuō)汁胆,包含 Client、 Server霜幼、 ServiceManager 以及 Binder 驅(qū)動(dòng)嫩码,其中 ServiceManager 用于管理系統(tǒng)中的各種服務(wù)。
- Binder 在 framework 層進(jìn)行了封裝罪既,通過(guò) JNI 技術(shù)與 Native(C/C++)層的 Binder 架構(gòu)通信铸题。
- Binder 在 Native 層以 ioctl 的方式與 Binder 驅(qū)動(dòng)通信。
5 Binder機(jī)制
- 首先需要注冊(cè)服務(wù)端琢感,只有注冊(cè)了服務(wù)端丢间,客戶端才有通訊的目標(biāo),服務(wù)端通過(guò) ServiceManager 注冊(cè)服務(wù)驹针,注冊(cè)的過(guò)程就是向 Binder 驅(qū)動(dòng)的全局鏈表 binder_procs 中插入服務(wù)端的信息(binder_proc 結(jié)構(gòu)體烘挫,每個(gè) binder_proc 結(jié)構(gòu)體中都有 todo 任務(wù)隊(duì)列),然后向 ServiceManager 的 svcinfo 列表中緩存一下注冊(cè)的服務(wù)柬甥。
- 有了服務(wù)端饮六,客戶端就可以跟服務(wù)端通訊了,通訊之前需要先獲取到服務(wù)苛蒲,拿到服務(wù)的代理卤橄,也可以理解為引用。比如下面的代碼:
//獲取WindowManager服務(wù)引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
獲取服務(wù)端的方式就是通過(guò) ServiceManager 向 svcinfo 列表中查詢一下返回服務(wù)端的代理臂外,svcinfo 列表就是所有已注冊(cè)服務(wù)的通訊錄窟扑,保存了所有注冊(cè)的服務(wù)信息。
- 有了服務(wù)端的引用我們就可以向服務(wù)端發(fā)送請(qǐng)求了漏健,通過(guò) BinderProxy 將我們的請(qǐng)求參數(shù)發(fā)送給 ServiceManager嚎货,通過(guò)共享內(nèi)存的方式使用內(nèi)核方法 copy_from_user() 將我們的參數(shù)先拷貝到內(nèi)核空間,并建立用戶空間到內(nèi)核空間的內(nèi)存映射關(guān)系蔫浆,這時(shí)我們的客戶端進(jìn)入等待狀態(tài)殖属,然后 Binder 驅(qū)動(dòng)向服務(wù)端的 todo 隊(duì)列里面插入一條事務(wù),執(zhí)行完之后把執(zhí)行結(jié)果通過(guò) copy_to_user() 將內(nèi)核的結(jié)果通過(guò)內(nèi)存映射關(guān)系映射到用戶空間克懊,喚醒等待的客戶端并把結(jié)果響應(yīng)回來(lái)忱辅,這樣就完成了一次通訊七蜘。
6 Binder驅(qū)動(dòng)
我們先來(lái)看下用戶空間與內(nèi)核空間的交互
通過(guò)系統(tǒng)調(diào)用谭溉,用戶空間可以訪問(wèn)內(nèi)核空間墙懂,那么如果一個(gè)用戶空間想與另外一個(gè)用戶空間進(jìn)行通信怎么辦呢?很自然想到的是讓操作系統(tǒng)內(nèi)核添加支持扮念;傳統(tǒng)的 Linux 通信機(jī)制损搬,比如 Socket,管道等都是內(nèi)核支持的柜与;但是 Binder 并不是 Linux 內(nèi)核的一部分巧勤,它是怎么做到訪問(wèn)內(nèi)核空間的呢? Linux 的動(dòng)態(tài)可加載內(nèi)核模塊(Loadable Kernel Module弄匕,LKM)機(jī)制解決了這個(gè)問(wèn)題颅悉;模塊是具有獨(dú)立功能的程序,它可以被單獨(dú)編譯迁匠,但不能獨(dú)立運(yùn)行剩瓶。它在運(yùn)行時(shí)被鏈接到內(nèi)核作為內(nèi)核的一部分在內(nèi)核空間運(yùn)行。這樣城丧,Android系統(tǒng)可以通過(guò)添加一個(gè)內(nèi)核模塊運(yùn)行在內(nèi)核空間延曙,用戶進(jìn)程之間的通過(guò)這個(gè)模塊作為橋梁,就可以完成通信了亡哄。
在 Android 系統(tǒng)中枝缔,這個(gè)運(yùn)行在內(nèi)核空間的,負(fù)責(zé)各個(gè)用戶進(jìn)程通過(guò) Binder 通信的內(nèi)核模塊叫做 Binder 驅(qū)動(dòng);
熟悉了上面這些概念蚊惯,我們?cè)賮?lái)看下上面的圖愿卸,用戶空間中 binder_open(), binder_mmap(), binder_ioctl() 這些方法通過(guò) system call 來(lái)調(diào)用內(nèi)核空間 Binder 驅(qū)動(dòng)中的方法。內(nèi)核空間與用戶空間共享內(nèi)存通過(guò) copy_from_user(), copy_to_user() 內(nèi)核方法來(lái)完成用戶空間與內(nèi)核空間內(nèi)存的數(shù)據(jù)傳輸截型。 Binder驅(qū)動(dòng)中有一個(gè)全局的 binder_procs 鏈表保存了服務(wù)端的進(jìn)程信息擦酌。
那么在 Android 系統(tǒng)中用戶進(jìn)程之間是如何通過(guò)這個(gè)內(nèi)核模塊(Binder 驅(qū)動(dòng))來(lái)實(shí)現(xiàn)通信的呢?難道是和前面說(shuō)的傳統(tǒng) IPC 機(jī)制一樣菠劝,先將數(shù)據(jù)從發(fā)送方進(jìn)程拷貝到內(nèi)核緩存區(qū)赊舶,然后再將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到接收方進(jìn)程,通過(guò)兩次拷貝來(lái)實(shí)現(xiàn)嗎赶诊?顯然不是笼平,否則也不會(huì)有開篇所說(shuō)的 Binder 在性能方面的優(yōu)勢(shì)了。
Linux 下的另一個(gè)概念:內(nèi)存映射
Binder IPC 機(jī)制中涉及到的內(nèi)存映射通過(guò) mmap() 來(lái)實(shí)現(xiàn)舔痪,mmap() 是操作系統(tǒng)中一種內(nèi)存映射的方法寓调,該函數(shù)經(jīng)過(guò)系統(tǒng)調(diào)用最終會(huì)調(diào)用到binder驅(qū)動(dòng)的binder_mmap() 函數(shù)。內(nèi)存映射簡(jiǎn)單的講就是將用戶空間的一塊內(nèi)存區(qū)域映射到內(nèi)核空間锄码。映射關(guān)系建立后夺英,用戶對(duì)這塊內(nèi)存區(qū)域的修改可以直接反應(yīng)到內(nèi)核空間晌涕;反之內(nèi)核空間對(duì)這段區(qū)域的修改也能直接反應(yīng)到用戶空間。內(nèi)存映射能減少數(shù)據(jù)拷貝次數(shù)痛悯,實(shí)現(xiàn)用戶空間和內(nèi)核空間的高效互動(dòng)余黎。兩個(gè)空間各自的修改能直接反映在映射的內(nèi)存區(qū)域,從而被對(duì)方空間及時(shí)感知载萌。
7 Binder 進(jìn)程與線程
對(duì)于底層Binder驅(qū)動(dòng)惧财,通過(guò) binder_procs 鏈表記錄所有創(chuàng)建的 binder_proc 結(jié)構(gòu)體,binder 驅(qū)動(dòng)層的每一個(gè) binder_proc 結(jié)構(gòu)體都與用戶空間的一個(gè)用于 binder 通信的進(jìn)程一一對(duì)應(yīng)扭仁,且每個(gè)進(jìn)程有且只有一個(gè) ProcessState 對(duì)象垮衷,這是通過(guò)單例模式來(lái)保證的。在每個(gè)進(jìn)程中可以有很多個(gè)線程乖坠,每個(gè)線程對(duì)應(yīng)一個(gè) IPCThreadState 對(duì)象搀突,IPCThreadState 對(duì)象也是單例模式,即一個(gè)線程對(duì)應(yīng)一個(gè) IPCThreadState 對(duì)象熊泵,在 Binder 驅(qū)動(dòng)層也有與之相對(duì)應(yīng)的結(jié)構(gòu)仰迁,那就是 Binder_thread 結(jié)構(gòu)體。在 binder_proc 結(jié)構(gòu)體中通過(guò)成員變量 rb_root threads戈次,來(lái)記錄當(dāng)前進(jìn)程內(nèi)所有的 binder_thread轩勘。
Binder 線程池:每個(gè) Server 進(jìn)程在啟動(dòng)時(shí)創(chuàng)建一個(gè) binder 線程池,并向其中注冊(cè)一個(gè) Binder 線程怯邪;之后 Server 進(jìn)程也可以向 binder 線程池注冊(cè)新的線程绊寻,或者 Binder 驅(qū)動(dòng)在探測(cè)到?jīng)]有空閑 binder 線程時(shí)主動(dòng)向 Server 進(jìn)程注冊(cè)新的的 binder 線程。對(duì)于一個(gè) Server 進(jìn)程有一個(gè)最大 Binder 線程數(shù)限制悬秉,默認(rèn)為16個(gè) binder 線程澄步,例如 Android 的 system_server 進(jìn)程就存在16個(gè)線程。對(duì)于所有 Client 端進(jìn)程的 binder 請(qǐng)求都是交由 Server 端進(jìn)程的 binder 線程來(lái)處理的和泌。
8 ServiceManager 啟動(dòng)
ServiceManager提供了向Binder 驅(qū)動(dòng)查詢服務(wù)和注冊(cè)服務(wù)的功能村缸。
- ServiceManager 分為 framework 層和 native 層,framework 層只是對(duì) native 層進(jìn)行了封裝方便調(diào)用武氓,圖上展示的是 native 層的 ServiceManager 啟動(dòng)過(guò)程梯皿。
- ServiceManager 的啟動(dòng)是系統(tǒng)在開機(jī)時(shí),init 進(jìn)程解析 init.rc 文件調(diào)用 service_manager.c 中的 main() 方法入口啟動(dòng)的县恕。 native 層有一個(gè) binder.c 封裝了一些與 Binder 驅(qū)動(dòng)交互的方法东羹。
- ServiceManager 的啟動(dòng)分為三步,首先打開驅(qū)動(dòng)創(chuàng)建全局鏈表 binder_procs忠烛,然后將自己當(dāng)前進(jìn)程信息保存到 binder_procs 鏈表属提,最后開啟 loop 不斷的處理共享內(nèi)存中的數(shù)據(jù),并處理 BR_xxx 命令(ioctl 的命令,BR 可以理解為 binder reply 驅(qū)動(dòng)處理完的響應(yīng))冤议。
9 ServiceManager 注冊(cè)服務(wù)
- 注冊(cè) MediaPlayerService 服務(wù)端斟薇,我們通過(guò) ServiceManager 的 addService() 方法來(lái)注冊(cè)服務(wù)。
- 首先 ServiceManager 向 Binder 驅(qū)動(dòng)發(fā)送 BC_TRANSACTION 命令(ioctl 的命令恕酸,BC 可以理解為 binder client 客戶端發(fā)過(guò)來(lái)的請(qǐng)求命令)攜帶 ADD_SERVICE_TRANSACTION 命令堪滨,同時(shí)注冊(cè)服務(wù)的線程進(jìn)入等待狀態(tài) waitForResponse()。 Binder 驅(qū)動(dòng)收到請(qǐng)求命令向 ServiceManager 的 todo 隊(duì)列里面添加一條注冊(cè)服務(wù)的事務(wù)尸疆。事務(wù)的任務(wù)就是創(chuàng)建服務(wù)端進(jìn)程 binder_node 信息并插入到 binder_procs 鏈表中椿猎。
- 事務(wù)處理完之后發(fā)送 BR_TRANSACTION 命令惶岭,ServiceManager 收到命令后向 svcinfo 列表中添加已經(jīng)注冊(cè)的服務(wù)寿弱。最后發(fā)送 BR_REPLY 命令喚醒等待的線程,通知注冊(cè)成功按灶。
10 ServiceManager 獲取服務(wù)
- 獲取服務(wù)的過(guò)程與注冊(cè)類似症革,相反的過(guò)程。通過(guò) ServiceManager 的 getService() 方法來(lái)注冊(cè)服務(wù)鸯旁。
- 首先 ServiceManager 向 Binder 驅(qū)動(dòng)發(fā)送 BC_TRANSACTION 命令攜帶 CHECK_SERVICE_TRANSACTION 命令噪矛,同時(shí)獲取服務(wù)的線程進(jìn)入等待狀態(tài) waitForResponse()。
- Binder 驅(qū)動(dòng)收到請(qǐng)求命令向 ServiceManager 的發(fā)送 BR_TRANSACTION 查詢已注冊(cè)的服務(wù)铺罢,查詢到直接響應(yīng) BR_REPLY 喚醒等待的線程艇挨。若查詢不到將與 binder_procs 鏈表中的服務(wù)進(jìn)行一次通訊再響應(yīng)。
11 進(jìn)行一次完整通訊
我們?cè)谑褂?Binder 時(shí)基本都是調(diào)用 framework 層封裝好的方法韭赘,AIDL 就是 framework 層提供的傻瓜式是使用方式缩滨。假設(shè)服務(wù)已經(jīng)注冊(cè)完,我們來(lái)看看客戶端怎么執(zhí)行服務(wù)端的方法泉瞻。
- 首先我們通過(guò) ServiceManager 獲取到服務(wù)端的 BinderProxy 代理對(duì)象脉漏,通過(guò)調(diào)用 BinderProxy 將參數(shù),方法標(biāo)識(shí)(例如:TRANSACTION_test袖牙,AIDL中自動(dòng)生成)傳給 ServiceManager侧巨,同時(shí)客戶端線程進(jìn)入等待狀態(tài)。
- ServiceManager 將用戶空間的參數(shù)等請(qǐng)求數(shù)據(jù)復(fù)制到內(nèi)核空間鞭达,并向服務(wù)端插入一條執(zhí)行執(zhí)行方法的事務(wù)司忱。事務(wù)執(zhí)行完通知 ServiceManager 將執(zhí)行結(jié)果從內(nèi)核空間復(fù)制到用戶空間,并喚醒等待的線程畴蹭,響應(yīng)結(jié)果坦仍。
參考鏈接:
一篇文章了解相見恨晚的 Android Binder 進(jìn)程間通訊機(jī)制
寫給 Android 應(yīng)用工程師的 Binder 原理剖析
Android Bander設(shè)計(jì)與實(shí)現(xiàn) - 設(shè)計(jì)篇