1磅轻、概述
Linux傳統(tǒng)IPC機(jī)制主要有已下幾種:管道、消息隊(duì)列僧免、共享內(nèi)存Socket等。消息隊(duì)列和管道采用存儲(chǔ)-轉(zhuǎn)發(fā)方式捏浊,即數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中懂衩,然后再從內(nèi)核緩存區(qū)拷貝到接收方緩存區(qū),至少有兩次拷貝過程金踪。共享內(nèi)存雖然無需拷貝浊洞,但控制復(fù)雜,難以使用热康。socket作為一款通用接口沛申,其傳輸效率低,開銷大姐军,主要用在跨網(wǎng)絡(luò)的進(jìn)程間通信和本機(jī)上進(jìn)程間的低速通信铁材。
同時(shí)從安全性角度考慮尖淘,Android作為一個(gè)開放式,擁有眾多開發(fā)者的平臺(tái)著觉,應(yīng)用程序的來源廣泛村生,確保智能終端的安全是非常重要的。傳統(tǒng)IPC沒有任何安全措施饼丘,完全依賴上層協(xié)議來確保趁桃。首先傳統(tǒng)IPC的接收方無法獲得對(duì)方進(jìn)程可靠的UID/PID(用戶ID/進(jìn)程ID),從而無法鑒別對(duì)方身份肄鸽。Android為每個(gè)安裝好的應(yīng)用程序分配了自己的UID卫病,故進(jìn)程的UID是鑒別進(jìn)程身份的重要標(biāo)志。使用傳統(tǒng)IPC只能由用戶在數(shù)據(jù)包里填入U(xiǎn)ID/PID典徘,但這樣不可靠蟀苛,容易被惡意程序利用〈澹可靠的身份標(biāo)記只有由IPC機(jī)制本身在內(nèi)核中添加帜平。其次傳統(tǒng)IPC訪問接入點(diǎn)是開放的,無法建立私有通道梅鹦。比如命名管道的名稱裆甩、system V的鍵值、socket的ip地址或文件名都是開放的齐唆,只要知道這些接入點(diǎn)的程序都可以和對(duì)端建立連接嗤栓,不管怎樣都無法阻止惡意程序通過猜測(cè)接收方地址獲得連接。
由于傳統(tǒng)的IPC機(jī)制有這樣那樣不讓人滿意的原因箍邮。Android自己開發(fā)了一套進(jìn)程間通信機(jī)制Binder通信抛腕。Binder機(jī)制起源于一個(gè)簡(jiǎn)單的想法:將申請(qǐng)服務(wù)的請(qǐng)求和對(duì)應(yīng)的響應(yīng)信息,寫入一個(gè)所有進(jìn)程均能夠訪問的地址空間中媒殉。當(dāng)進(jìn)程需要使用這些數(shù)據(jù)時(shí)担敌,只需要訪問對(duì)應(yīng)的內(nèi)存地址,以減小內(nèi)容復(fù)制引入的開銷廷蓉。為此全封,Binder機(jī)制利用kernel空間作為共享區(qū)域,并由Binder driver來建立起每個(gè)進(jìn)程的內(nèi)存地址與kernel空間中存儲(chǔ)地址的映射桃犬。從而使數(shù)據(jù)只要拷貝一次就可以刹悴。同時(shí)為發(fā)送方添加UID/PID身份,既支持實(shí)名Binder也支持匿名Binder攒暇,提高安全性土匀。
2、Binder架構(gòu)
Binder通信采用C/S架構(gòu)形用,從組件視角來說就轧,包含Client证杭、Server、ServiceManager以及Binder驅(qū)動(dòng)妒御,其中ServiceManager用于管理系統(tǒng)中的各種服務(wù)解愤。如上圖所示Binder在Framework層和Native層分別有對(duì)應(yīng)的客戶端(Client)、服務(wù)(Server)的和服務(wù)管理器(Service Manager)乎莉。同時(shí)在Kernel層(內(nèi)核空間)有Binder的驅(qū)動(dòng)設(shè)備送讲。
這四個(gè)角色的作用分別是:
① Client進(jìn)程:使用服務(wù)的進(jìn)程。
② Server進(jìn)程:提供服務(wù)的進(jìn)程惋啃。
③ ServiceManager進(jìn)程:ServiceManager的作用是將字符形式的Binder名字轉(zhuǎn)化成Client中對(duì)該Binder的引用哼鬓,使得Client能夠通過Binder名字獲得對(duì)Server中Binder實(shí)體的引用。
④ Binder驅(qū)動(dòng):驅(qū)動(dòng)負(fù)責(zé)進(jìn)程之間Binder通信的建立边灭,Binder在進(jìn)程之間的傳遞魄宏,Binder引用計(jì)數(shù)管理,數(shù)據(jù)包在進(jìn)程之間的傳遞和交互等一系列底層支持存筏。
3、Binder運(yùn)行機(jī)制
3.1 注冊(cè)服務(wù):
首先需要注冊(cè)服務(wù)端味榛,只有注冊(cè)了服務(wù)端椭坚,客戶端才有通訊的目標(biāo),服務(wù)端通過 ServiceManager 注冊(cè)服務(wù)搏色,注冊(cè)的過程就是向 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ù)频轿。
3.2 查詢服務(wù):
注冊(cè)完服務(wù)后客戶端就能夠查詢并獲取注冊(cè)了的服務(wù)了垂涯。客戶端向ServiceManager 發(fā)起獲取服務(wù)的請(qǐng)求航邢,傳遞要獲取服務(wù)的名稱耕赘。ServiceManager 從服務(wù)列表中查找到Client需要的對(duì)應(yīng)服務(wù)信息,將該服務(wù)的代理Binder(BinderProxy)返回給客戶端膳殷。其實(shí)查詢服務(wù)相當(dāng)于ServiceManager 作為服務(wù)端操骡,客戶端進(jìn)行使用。所以這也是一次Binder使用服務(wù)的過程赚窃。
3.3 使用服務(wù):
使用服務(wù)的過程如上圖册招。
① Binder驅(qū)動(dòng)為跨進(jìn)程通信做準(zhǔn)備:通過調(diào)用mmap()系統(tǒng)函數(shù)實(shí)現(xiàn)內(nèi)存映射。在Binder驅(qū)動(dòng)中創(chuàng)建一塊接收緩存區(qū)勒极。同時(shí)將內(nèi)核緩存區(qū)地址和Server端中用戶空間一塊地址同時(shí)都映射到該接收緩存區(qū)中是掰。這時(shí)候就創(chuàng)建了虛擬區(qū)間和映射的關(guān)系。
② Client進(jìn)程將數(shù)據(jù)發(fā)送到Server進(jìn)程辱匿。Client進(jìn)程通過調(diào)用copy_from_user()發(fā)送數(shù)據(jù)拷貝到內(nèi)核中(Binder驅(qū)動(dòng))的緩存區(qū)中键痛,此時(shí)Client發(fā)起請(qǐng)求的線程會(huì)被掛起炫彩。由于在①中構(gòu)建了映射關(guān)系,此時(shí)相當(dāng)于也將數(shù)據(jù)發(fā)送到了Server端的用戶空間中散休。之后Binder驅(qū)動(dòng)通知Server端進(jìn)程執(zhí)行解包媒楼。
③ Server進(jìn)程根據(jù)Client進(jìn)程發(fā)送來的數(shù)據(jù),調(diào)用目標(biāo)方法戚丸。收到Binder驅(qū)動(dòng)通知后划址,Server進(jìn)程對(duì)數(shù)據(jù)進(jìn)行解包,并調(diào)用相關(guān)方法處理限府。
④ Server進(jìn)程將目標(biāo)方法處理結(jié)果返回給Client進(jìn)程夺颤。將處理結(jié)果放回自己的共享空間(即①中映射的Binder驅(qū)動(dòng)緩存區(qū)中)。Binder驅(qū)動(dòng)通知Client進(jìn)程獲取返回結(jié)果胁勺,此時(shí)②中被掛起的線程會(huì)被重新喚醒世澜。Client進(jìn)程通過系統(tǒng)調(diào)用copy_to_user(),從內(nèi)核緩存區(qū)拷貝Server進(jìn)程返回的結(jié)果。
從上面使用服務(wù)的過程可以看到署穗,整個(gè)過程只拷貝了一次發(fā)送的數(shù)據(jù)和一次接收的數(shù)據(jù)寥裂。而正如開頭所述,消息隊(duì)列和管道這兩種IPC拷貝次數(shù)為2次案疲。
Tips:
Binder模型的線程管理采用Binder驅(qū)動(dòng)線程池封恰,由Binder驅(qū)動(dòng)進(jìn)行管理,而不是由Server進(jìn)程來管理褐啡。而一個(gè)進(jìn)程的Binder線程默認(rèn)線程數(shù)默認(rèn)最大是16诺舔。也就是說Client的請(qǐng)求數(shù)一旦超過16個(gè)會(huì)被阻塞直到有空間的Binder線程。