1. 程式人生 > >Android Binder分析

Android Binder分析

Binder通訊模型

Binder的優勢

實現方式

        Binder使用Client-Server通訊方式:一個程序作為Server提供諸如視訊/音訊解碼,視訊捕獲,地址本查詢,網路連線等服務;多個程序作為Client向Server發起服務請求,獲得所需要的服務。要想實現Client-Server通信據必須實現以下兩點:一是server必須有確定的訪問接入點或者說地址來接受Client的請求,並且Client可以通過某種途徑獲知Server的地址;二是制定Command-Reply協議來傳輸資料。例如在網路通訊中Server的訪問接入點就是Server主機的IP地址+埠號,傳輸協議為TCP協議。對Binder而言,Binder可以看成Server提供的實現某個特定服務的訪問接入點, Client通過這個‘地址’向Server傳送請求來使用該服務;對Client而言,Binder可以看成是通向Server的管道入口,要想和某個Server通訊首先必須建立這個管道並獲得管道入口。

效能優化

        如果是傳統的Linux IPC方式中,socket作為一款通用介面,其傳輸效率低,開銷大,主要用在跨網路的程序間通訊和本機上程序間的低速通訊。訊息佇列和管道採用儲存-轉發方式,即資料先從傳送方快取區拷貝到核心開闢的快取區中,然後再從核心快取區拷貝到接收方快取區,至少有兩次拷貝過程。共享記憶體雖然無需拷貝,但控制複雜,難以使用。

        舉個例子如,Client要將一塊記憶體資料傳遞給Server,一般的做法是,Client將這塊資料從它的程序空間拷貝到核心空間中,然後核心再將這個資料從核心空間拷貝到Server的程序空間,這樣,Server就可以訪問這個資料了。但是在這種方法中,執行了兩次記憶體拷貝操作。所以Binder設計時採用了折衷的方式,只需要把Client程序空間的資料拷貝一次到核心空間,然後Server與核心共享這個資料就可以了,整個過程只需要執行一次記憶體拷貝,提高了效率。同時這樣更有C/S架構的模型,方便管理。

Binder 通訊模型

        從英文字面上意思看,Binder具有粘結劑的意思,那麼它把什麼東西粘結在一起呢?在Android系統的Binder機制中,由一系統元件組成,分別是Client、Server、Service Manager和Binder驅動程式,其中Client、Server和Service Manager執行在使用者空間,Binder驅動程式執行核心空間。Binder就是一種把這四個元件粘合在一起的粘結劑了,其中,核心元件便是Binder驅動程式了,Service Manager提供了輔助管理的功能,Client和Server正是在Binder驅動和Service Manager提供的基礎設施上,進行Client-Server之間的通訊。這四個角色的關係和網際網路類似:Server是伺服器,Client是客戶終端,Service Manager是域名伺服器(DNS),Binder驅動是路由器。

Binder驅動

        和路由器一樣,Binder驅動雖然默默無聞,卻是通訊的核心。儘管名叫‘驅動’,實際上和硬體裝置沒有任何關係,只是實現方式和裝置驅動程式是一樣的:它工作於核心態,提供open(),mmap(),poll(),ioctl()等標準檔案操作,以字元驅動裝置中的misc設備註冊在裝置目錄/dev下,使用者通過/dev/binder訪問該它。驅動負責程序之間Binder通訊的建立,Binder在程序之間的傳遞,Binder引用計數管理,資料包在程序之間的傳遞和互動等一系列底層支援。驅動和應用程式之間定義了一套介面協議,主要功能由ioctl()介面實現,不提供read(),write()介面,因為ioctl()靈活方便,且能夠一次呼叫實現先寫後讀以滿足同步互動,而不必分別呼叫write()和read()。Binder驅動的程式碼每個分支位置不一樣,再加上我也沒有下核心的程式碼,這裡先給個4.4的Binder.c的地址,有興趣的可以自己研究。

ServiceManager 與實名Binder

        和DNS類似,ServiceManager的作用是將字元形式的Binder名字轉化成Client中對該Binder的引用,使得Client能夠通過Binder名字獲得對Server中Binder實體的引用。註冊了名字的Binder叫實名Binder,就象每個網站除了有IP地址外還有自己的網址。Server建立了Binder實體,為其取一個字元形式,可讀易記的名字,將這個Binder連同名字以資料包的形式通過Binder驅動傳送給ServiceManager,通知ServiceManager註冊一個名叫張三的Binder,它位於某個Server中。驅動為這個穿過程序邊界的Binder建立位於核心中的實體節點以及ServiceManager對實體的引用,將名字及新建的引用打包傳遞給ServiceManager。ServiceManager收資料包後,從中取出名字和引用填入一張查詢表中。

        細心的讀者可能會發現其中的蹊蹺:ServiceManager是一個程序,Server是另一個程序,Server向ServiceManager註冊Binder必然會涉及程序間通訊。當前實現的是程序間通訊卻又要用到程序間通訊,這就好象蛋可以孵出雞前提卻是要找只雞來孵蛋。Binder的實現比較巧妙:預先創造一隻雞來孵蛋:ServiceManager和其它程序同樣採用Binder通訊,ServiceManager是Server端,有自己的Binder物件(實體),其它程序都是Client,需要通過這個Binder的引用來實現Binder的註冊,查詢和獲取。ServiceManager提供的Binder比較特殊,它沒有名字也不需要註冊,當一個程序使用BINDER_SET_CONTEXT_MGR命令將自己註冊成ServiceManager(會用到ioctl(fd, cmd, arg)函式,cmd為BINDER_SET_CONTEXT_MGR)時Binder驅動會自動為它建立Binder實體(這就是那隻預先造好的雞)。其次這個Binder的引用在所有Client中都固定為0而無須通過其它手段獲得。也就是說,一個Server若要向ServiceManager註冊自己Binder就必需通過0(即NULL指標)這個引用號和ServiceManager的Binder通訊。類比網路通訊,0號引用就好比域名伺服器的地址,你必須預先手工或動態配置好。要注意這裡說的Client是相對ServiceManager而言的,一個應用程式可能是個提供服務的Server,但對ServiceManager來說它仍然是個Client。

Client 獲得實名Binder的引用

        Server向ServiceManager註冊了Binder實體及其名字後,Client就可以通過名字獲得該Binder的引用了。Client也利用保留的0號引用向ServiceManager請求訪問某個Binder:我申請獲得名字叫張三的Binder的引用。ServiceManager收到這個連線請求,從請求資料包裡獲得Binder的名字,在查詢表裡找到該名字對應的條目,從條目中取出Binder的引用,將該引用作為回覆傳送給發起請求的Client。從面向物件的角度,這個Binder物件現在有了兩個引用:一個位於ServiceManager中,一個位於發起請求的Client中。如果接下來有更多的Client請求該Binder,系統中就會有更多的引用指向該Binder,就象java裡一個物件存在多個引用一樣。而且類似的這些指向Binder的引用是強型別,從而確保只要有引用Binder實體就不會被釋放掉。通過以上過程可以看出,ServiceManager象個火車票代售點,收集了所有火車的車票,可以通過它購買到乘坐各趟火車的票-得到某個Binder的引用。

匿名 Binder

        並不是所有Binder都需要註冊給ServiceManager廣而告之的。Server端可以通過已經建立的Binder連線將建立的Binder實體傳給Client,當然這條已經建立的Binder連線必須是通過實名Binder實現。如果我們是從事application開發,跨程序的自己手寫AIDL檔案,或者相同程序的bindService自己新增一個繼承Binder的子類,那麼這個Binder沒有向ServiceManager註冊名字,所以是個匿名Binder。Client將會收到這個匿名Binder的引用,通過這個引用向位於Server中的實體傳送請求。匿名Binder為通訊雙方建立一條私密通道,只要Server沒有把匿名Binder發給別的程序,別的程序就無法通過窮舉或猜測等任何方式獲得該Binder的引用,向該Binder傳送請求。