毫不夸張地說(shuō),Binder是Android系統(tǒng)中最重要的特性之一;正如其名“粘合劑”所喻,它是系統(tǒng)間各個(gè)組件的橋梁,Android系統(tǒng)的開(kāi)放式設(shè)計(jì)也很大程度上得益于這種及其方便的跨進(jìn)程通信機(jī)制。
理解Binder對(duì)于理解整個(gè)Android系統(tǒng)有著非常重要的作用,Android系統(tǒng)的四大組件,AMS,PMS等系統(tǒng)服務(wù)無(wú)一不與Binder掛鉤;如果對(duì)Binder不甚了解,那么就很難了解這些系統(tǒng)機(jī)制,從而僅僅浮游與表面,不懂Binder你都不好意思說(shuō)自己會(huì)Android開(kāi)發(fā);要深入Android,Binder是必須邁出的一步。
現(xiàn)在網(wǎng)上有不少資料介紹Binder,個(gè)人覺(jué)得最好的兩篇如下:
Binder設(shè)計(jì)與實(shí)現(xiàn)
Android進(jìn)程間通信(IPC)機(jī)制Binder簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃系列
其中, 《Binder設(shè)計(jì)與實(shí)現(xiàn)》以一種宏觀的角度解釋了Android系統(tǒng)中的Binder機(jī)制,文章如行云流水;如果對(duì)于Binder有一定的了解再來(lái)看著篇文章,有一種打通任督二脈的感覺(jué);每看一次理解就深一層。老羅的系列文章則從系統(tǒng)源碼角度深入分析了Binder的實(shí)現(xiàn)細(xì)節(jié);具有很大的參考意義;每當(dāng)對(duì)于Binder細(xì)節(jié)有疑惑,看一看他的書(shū)就迎刃而解。
但是遺憾的是,Binder機(jī)制終究不是三言兩語(yǔ)就能解釋清楚的,一上來(lái)就扒出源碼很可能深陷細(xì)節(jié)無(wú)法自拔,老羅的文章那不是一般的長(zhǎng),如果看不懂強(qiáng)行看很容易睡著;勉強(qiáng)看完還是云里霧里;相反如果直接大談特談Binder的設(shè)計(jì),那么完全就是不知所云;因此上述兩篇文章對(duì)于初學(xué)者并不友好,本文不會(huì)深入源碼細(xì)節(jié),也不會(huì)對(duì)于Binder的設(shè)計(jì)高談闊論;重點(diǎn)如下:
一些Linux的預(yù)備知識(shí)
Binder到底是什么?
Binder機(jī)制是如何跨進(jìn)程的?
一次Binder通信的基本流程是什么樣?
深入理解Java層的Binder
讀完本文,你應(yīng)該對(duì)于Java層的AIDL了如指掌,對(duì)于Binder也會(huì)有一個(gè)大體上的認(rèn)識(shí);再深入學(xué)習(xí)就得靠自己了,本人推薦的Binder學(xué)習(xí)路徑如下:
先學(xué)會(huì)熟練使用AIDL進(jìn)行跨進(jìn)程通信(簡(jiǎn)單來(lái)說(shuō)就是遠(yuǎn)程Service)
看完本文
看Android文檔,Parcel, IBinder, Binder等涉及到跨進(jìn)程通信的類
不依賴AIDL工具,手寫(xiě)遠(yuǎn)程Service完成跨進(jìn)程通信
看《Binder設(shè)計(jì)與實(shí)現(xiàn)》
看老羅的博客或者書(shū)(書(shū)結(jié)構(gòu)更清晰)
再看《Binder設(shè)計(jì)與實(shí)現(xiàn)》
學(xué)習(xí)Linux系統(tǒng)相關(guān)知識(shí);自己看源碼。
背景知識(shí)
為了理解Binder我們先澄清一些概念。為什么需要跨進(jìn)程通信(IPC),怎么做到跨進(jìn)程通信?為什么是Binder?
由于Android系統(tǒng)基于Linux內(nèi)核,因此有必要了解相關(guān)知識(shí)。
進(jìn)程隔離
進(jìn)程隔離是為保護(hù)操作系統(tǒng)中進(jìn)程互不干擾而設(shè)計(jì)的一組不同硬件和軟件的技術(shù)。這個(gè)技術(shù)是為了避免進(jìn)程A寫(xiě)入進(jìn)程B的情況發(fā)生。 進(jìn)程的隔離實(shí)現(xiàn),使用了虛擬地址空間。進(jìn)程A的虛擬地址和進(jìn)程B的虛擬地址不同,這樣就防止進(jìn)程A將數(shù)據(jù)信息寫(xiě)入進(jìn)程B。
以上來(lái)自維基百科;操作系統(tǒng)的不同進(jìn)程之間,數(shù)據(jù)不共享;對(duì)于每個(gè)進(jìn)程來(lái)說(shuō),它都天真地以為自己獨(dú)享了整個(gè)系統(tǒng),完全不知道其他進(jìn)程的存在;(有關(guān)虛擬地址,請(qǐng)自行查閱)因此一個(gè)進(jìn)程需要與另外一個(gè)進(jìn)程通信,需要某種系統(tǒng)機(jī)制才能完成。
用戶空間/內(nèi)核空間
詳細(xì)解釋可以參考Kernel Space Definition;簡(jiǎn)單理解如下:
Linux Kernel是操作系統(tǒng)的核心,獨(dú)立于普通的應(yīng)用程序,可以訪問(wèn)受保護(hù)的內(nèi)存空間,也有訪問(wèn)底層硬件設(shè)備的所有權(quán)限。
對(duì)于Kernel這么一個(gè)高安全級(jí)別的東西,顯然是不容許其它的應(yīng)用程序隨便調(diào)用或訪問(wèn)的,所以需要對(duì)Kernel提供一定的保護(hù)機(jī)制,這個(gè)保護(hù)機(jī)制用來(lái)告訴那些應(yīng)用程序,你只可以訪問(wèn)某些許可的資源,不許可的資源是拒絕被訪問(wèn)的,于是就把Kernel和上層的應(yīng)用程序抽像的隔離開(kāi),分別稱之為Kernel Space和User Space。
系統(tǒng)調(diào)用/內(nèi)核態(tài)/用戶態(tài)
雖然從邏輯上抽離出用戶空間和內(nèi)核空間;但是不可避免的的是,總有那么一些用戶空間需要訪問(wèn)內(nèi)核的資源;比如應(yīng)用程序訪問(wèn)文件,網(wǎng)絡(luò)是很常見(jiàn)的事情,怎么辦呢?
Kernel space can be accessed by user processes only through the use of system calls.
用戶空間訪問(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)定。用戶軟件良莠不齊,要是它們亂搞把系統(tǒng)玩壞了怎么辦?因此對(duì)于某些特權(quán)操作必須交給安全可靠的內(nèi)核來(lái)執(zhí)行。
當(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í)行。當(dāng)進(jìn)程在執(zhí)行用戶自己的代碼時(shí),則稱其處于用戶運(yùn)行態(tài)(用戶態(tài))。即此時(shí)處理器在特權(quán)級(jí)最低的(3級(jí))用戶代碼中運(yùn)行。處理器在特權(quán)等級(jí)高的時(shí)候才能執(zhí)行那些特權(quán)CPU指令。
內(nèi)核模塊/驅(qū)動(dòng)
通過(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);
驅(qū)動(dòng)程序一般指的是設(shè)備驅(qū)動(dòng)程序(Device Driver),是一種可以使計(jì)算機(jī)和設(shè)備通信的特殊程序。相當(dāng)于硬件的接口,操作系統(tǒng)只有通過(guò)這個(gè)接口,才能控制硬件設(shè)備的工作;
驅(qū)動(dòng)就是操作硬件的接口,為了支持Binder通信過(guò)程,Binder使用了一種“硬件”,因此這個(gè)模塊被稱之為驅(qū)動(dòng)。
為什么使用Binder?
Android使用的Linux內(nèi)核擁有著非常多的跨進(jìn)程通信機(jī)制,比如管道,System V,Socket等;為什么還需要單獨(dú)搞一個(gè)Binder出來(lái)呢?主要有兩點(diǎn),性能和安全。在移動(dòng)設(shè)備上,廣泛地使用跨進(jìn)程通信肯定對(duì)通信機(jī)制本身提出了嚴(yán)格的要求;Binder相對(duì)出傳統(tǒng)的Socket方式,更加高效;另外,傳統(tǒng)的進(jìn)程通信方式對(duì)于通信雙方的身份并沒(méi)有做出嚴(yán)格的驗(yàn)證,只有在上層協(xié)議上進(jìn)行架設(shè);比如Socket通信ip地址是客戶端手動(dòng)填入的,都可以進(jìn)行偽造;而B(niǎo)inder機(jī)制從協(xié)議本身就支持對(duì)通信雙方做身份校檢,因而大大提升了安全性。這個(gè)也是Android權(quán)限模型的基礎(chǔ)。
Binder通信模型
對(duì)于跨進(jìn)程通信的雙方,我們姑且叫做Server進(jìn)程(簡(jiǎn)稱Server),Client進(jìn)程(簡(jiǎn)稱Client);由于進(jìn)程隔離的存在,它們之間沒(méi)辦法通過(guò)簡(jiǎn)單的方式進(jìn)行通信,那么Binder機(jī)制是如何進(jìn)行的呢?
回想一下日常生活中我們通信的過(guò)程:假設(shè)A和B要進(jìn)行通信,通信的媒介是打電話(A是Client,B是Server);A要給B打電話,必須知道B的號(hào)碼,這個(gè)號(hào)碼怎么獲取呢?通信錄.
這個(gè)通信錄就是一張表;內(nèi)容大致是:
1??B -> 12345676
2??C -> 12334354
先查閱通信錄,拿到B的號(hào)碼;才能進(jìn)行通信;否則,怎么知道應(yīng)該撥什么號(hào)碼?回想一下古老的電話機(jī),如果A要給B打電話,必須先連接通話中心,說(shuō)明給我接通B的電話;這時(shí)候通話中心幫他呼叫B;連接建立,就完成了通信。
另外,光有電話和通信錄是不可能完成通信的,沒(méi)有基站支持;信息根本無(wú)法傳達(dá)。
我們看到,一次電話通信的過(guò)程除了通信的雙方還有兩個(gè)隱藏角色:通信錄和基站。Binder通信機(jī)制也是一樣:兩個(gè)運(yùn)行在用戶空間的進(jìn)程要完成通信,必須借助內(nèi)核的幫助,這個(gè)運(yùn)行在內(nèi)核里面的程序叫做Binder驅(qū)動(dòng),它的功能類似于基站;通信錄呢,就是一個(gè)叫做ServiceManager的東西(簡(jiǎn)稱SM)
OK,Binder的通信模型就是這么簡(jiǎn)單,如下圖:

整個(gè)通信步驟如下:
SM建立(建立通信錄);首先有一個(gè)進(jìn)程向驅(qū)動(dòng)提出申請(qǐng)為SM;驅(qū)動(dòng)同意之后,SM進(jìn)程負(fù)責(zé)管理Service(注意這里是Service而不是Server,因?yàn)槿绻ㄐ胚^(guò)程反過(guò)來(lái)的話,那么原來(lái)的客戶端Client也會(huì)成為服務(wù)端Server)不過(guò)這時(shí)候通信錄還是空的,一個(gè)號(hào)碼都沒(méi)有。
各個(gè)Server向SM注冊(cè)(完善通信錄);每個(gè)Server端進(jìn)程啟動(dòng)之后,向SM報(bào)告,我是zhangsan, 要找我請(qǐng)返回0x1234(這個(gè)地址沒(méi)有實(shí)際意義,類比);其他Server進(jìn)程依次如此;這樣SM就建立了一張表,對(duì)應(yīng)著各個(gè)Server的名字和地址;就好比B與A見(jiàn)面了,說(shuō)存?zhèn)€我的號(hào)碼吧,以后找我撥打10086;
Client想要與Server通信,首先詢問(wèn)SM;請(qǐng)告訴我如何聯(lián)系z(mì)hangsan,SM收到后給他一個(gè)號(hào)碼0x1234;Client收到之后,開(kāi)心滴用這個(gè)號(hào)碼撥通了Server的電話,于是就開(kāi)始通信了。
那么Binder驅(qū)動(dòng)干什么去了呢?這里Client與SM的通信,以及Client與Server的通信,都會(huì)經(jīng)過(guò)驅(qū)動(dòng),驅(qū)動(dòng)在背后默默無(wú)聞,但是做著最重要的工作。驅(qū)動(dòng)是整個(gè)通信過(guò)程的核心,因此完成跨進(jìn)程通信的秘密全部隱藏在驅(qū)動(dòng)里面;這個(gè)我們稍后討論。
OK,上面就是整個(gè)Binder通信的基本模型;做了一個(gè)簡(jiǎn)單的類比,當(dāng)然也有一些不恰當(dāng)?shù)牡胤剑?比如通信錄現(xiàn)實(shí)中每個(gè)人都有一個(gè),但是SM整個(gè)系統(tǒng)只有一個(gè);基站也有很多個(gè),但是驅(qū)動(dòng)只有一個(gè));但是整體上就是這樣的;我們看到其實(shí)整個(gè)通信模型非常簡(jiǎn)單。
Binder機(jī)制跨進(jìn)程原理
上文給出了Binder的通信模型,指出了通信過(guò)程的四個(gè)角色: Client, Server, SM, driver; 但是我們?nèi)匀徊磺宄?b>Client到底是如何與Server完成通信的。
兩個(gè)運(yùn)行在用戶空間的進(jìn)程A和進(jìn)程B如何完成通信呢??jī)?nèi)核可以訪問(wèn)A和B的所有數(shù)據(jù);所以,最簡(jiǎn)單的方式是通過(guò)內(nèi)核做中轉(zhuǎn);假設(shè)進(jìn)程A要給進(jìn)程B發(fā)送數(shù)據(jù),那么就先把A的數(shù)據(jù)copy到內(nèi)核空間,然后把內(nèi)核空間對(duì)應(yīng)的數(shù)據(jù)copy到B就完成了;用戶空間要操作內(nèi)核空間,需要通過(guò)系統(tǒng)調(diào)用;剛好,這里就有兩個(gè)系統(tǒng)調(diào)用:copy_from_user,?copy_to_user。
但是,Binder機(jī)制并不是這么干的。講這么一段,是說(shuō)明進(jìn)程間通信并不是什么神秘的東西。那么,Binder機(jī)制是如何實(shí)現(xiàn)跨進(jìn)程通信的呢?
Binder驅(qū)動(dòng)為我們做了一切。
假設(shè)Client進(jìn)程想要調(diào)用Server進(jìn)程的object對(duì)象的一個(gè)方法add;對(duì)于這個(gè)跨進(jìn)程通信過(guò)程,我們來(lái)看看Binder機(jī)制是如何做的。 (通信是一個(gè)廣泛的概念,只要一個(gè)進(jìn)程能調(diào)用另外一個(gè)進(jìn)程里面某對(duì)象的方法,那么具體要完成什么通信內(nèi)容就很容易了。)

首先,Server進(jìn)程要向SM注冊(cè);告訴自己是誰(shuí),自己有什么能力;在這個(gè)場(chǎng)景就是Server告訴SM,它叫zhangsan,它有一個(gè)object對(duì)象,可以執(zhí)行add?操作;于是SM建立了一張表:zhangsan這個(gè)名字對(duì)應(yīng)進(jìn)程Server;
然后Client向SM查詢:我需要聯(lián)系一個(gè)名字叫做zhangsan的進(jìn)程里面的object對(duì)象;這時(shí)候關(guān)鍵來(lái)了:進(jìn)程之間通信的數(shù)據(jù)都會(huì)經(jīng)過(guò)運(yùn)行在內(nèi)核空間里面的驅(qū)動(dòng),驅(qū)動(dòng)在數(shù)據(jù)流過(guò)的時(shí)候做了一點(diǎn)手腳,它并不會(huì)給Client進(jìn)程返回一個(gè)真正的object對(duì)象,而是返回一個(gè)看起來(lái)跟object一模一樣的代理對(duì)象objectProxy,這個(gè)objectProxy也有一個(gè)add方法,但是這個(gè)add方法沒(méi)有Server進(jìn)程里面object對(duì)象的add方法那個(gè)能力;objectProxy的add只是一個(gè)傀儡,它唯一做的事情就是把參數(shù)包裝然后交給驅(qū)動(dòng)。(這里我們簡(jiǎn)化了SM的流程,見(jiàn)下文)
但是Client進(jìn)程并不知道驅(qū)動(dòng)返回給它的對(duì)象動(dòng)過(guò)手腳,畢竟偽裝的太像了,如假包換。Client開(kāi)開(kāi)心心地拿著objectProxy對(duì)象然后調(diào)用add方法;我們說(shuō)過(guò),這個(gè)add什么也不做,直接把參數(shù)做一些包裝然后直接轉(zhuǎn)發(fā)給Binder驅(qū)動(dòng)。
驅(qū)動(dòng)收到這個(gè)消息,發(fā)現(xiàn)是這個(gè)objectProxy;一查表就明白了:我之前用objectProxy替換了object發(fā)送給Client了,它真正應(yīng)該要訪問(wèn)的是object對(duì)象的add方法;于是Binder驅(qū)動(dòng)通知Server進(jìn)程,調(diào)用你的object對(duì)象的add方法,然后把結(jié)果發(fā)給我,Sever進(jìn)程收到這個(gè)消息,照做之后將結(jié)果返回驅(qū)動(dòng),驅(qū)動(dòng)然后把結(jié)果返回給Client進(jìn)程;于是整個(gè)過(guò)程就完成了。
由于驅(qū)動(dòng)返回的objectProxy與Server進(jìn)程里面原始的object是如此相似,給人感覺(jué)好像是直接把Server進(jìn)程里面的對(duì)象object傳遞到了Client進(jìn)程;因此,我們可以說(shuō)Binder對(duì)象是可以進(jìn)行跨進(jìn)程傳遞的對(duì)象
但事實(shí)上我們知道,Binder跨進(jìn)程傳輸并不是真的把一個(gè)對(duì)象傳輸?shù)搅肆硗庖粋€(gè)進(jìn)程;傳輸過(guò)程好像是Binder跨進(jìn)程穿越的時(shí)候,它在一個(gè)進(jìn)程留下了一個(gè)真身,在另外一個(gè)進(jìn)程幻化出一個(gè)影子(這個(gè)影子可以很多個(gè));Client進(jìn)程的操作其實(shí)是對(duì)于影子的操作,影子利用Binder驅(qū)動(dòng)最終讓真身完成操作。
理解這一點(diǎn)非常重要;務(wù)必仔細(xì)體會(huì)。另外,Android系統(tǒng)實(shí)現(xiàn)這種機(jī)制使用的是代理模式, 對(duì)于Binder的訪問(wèn),如果是在同一個(gè)進(jìn)程(不需要跨進(jìn)程),那么直接返回原始的Binder實(shí)體;如果在不同進(jìn)程,那么就給他一個(gè)代理對(duì)象(影子);我們?cè)谙到y(tǒng)源碼以及AIDL的生成代碼里面可以看到很多這種實(shí)現(xiàn)。
另外我們?yōu)榱撕?jiǎn)化整個(gè)流程,隱藏了SM這一部分驅(qū)動(dòng)進(jìn)行的操作;實(shí)際上,由于SM與Server通常不在一個(gè)進(jìn)程,Server進(jìn)程向SM注冊(cè)的過(guò)程也是跨進(jìn)程通信,驅(qū)動(dòng)也會(huì)對(duì)這個(gè)過(guò)程進(jìn)行暗箱操作:SM中存在的Server端的對(duì)象實(shí)際上也是代理對(duì)象,后面Client向SM查詢的時(shí)候,驅(qū)動(dòng)會(huì)給Client返回另外一個(gè)代理對(duì)象。Sever進(jìn)程的本地對(duì)象僅有一個(gè),其他進(jìn)程所擁有的全部都是它的代理。
一句話總結(jié)就是:Client進(jìn)程只不過(guò)是持有了Server端的代理;代理對(duì)象協(xié)助驅(qū)動(dòng)完成了跨進(jìn)程通信。