Android Binder機制原理

Android Binder機制原理

說起Binder,其實在我們開發中總是沒有感覺到它的存在,但是往往在很多地方都間接的在使用它,比如我們再startAcivity的時候,必定要去調用ActivityManagerService,因爲我們都知道Activity並不是我們直接new出來的,而是通過這個service創建出來的,然而這個service是處於另外的進程,我們就必須要通過binder獲取這個service,告知它去創建一個Activity出來。
諸如binder的使用,在很多地方比比皆是,媒體播放,視音頻捕獲,調用傳感器等等,

Binder是Android系統進程間通信(IPC)方式之一,但是爲什麼Android不使用Linux本生具有的IPC方式?

優點

  1. 性能方面
    在移動設備上(性能受限制的設備,比如要省電),廣泛地使用跨進程通信對通信機制的性能有嚴格的要求,Binder相對出傳統的Socket方式,更加高效。Binder數據拷貝只需要一次,而管道、消息隊列、Socket都需要2次,共享內存方式一次內存拷貝都不需要,但實現方式又比較複雜。
  2. 安全方面
    首先傳統IPC的接收方無法獲得對方進程可靠的UID和PID(用戶ID進程ID),從而無法鑑別對方身份。Android爲每個安裝好的應用程序分配了自己的UID,故進程的UID是鑑別進程身份的重要標誌。其實Binder機制主要是在本機內進行跨進程通信,而socket等IPC方式主要是用於跨網絡的通信手段,因而socket不能限制了入口和出口,所以從使用場景來看,socket給用戶提供的使用範圍更廣,而binder由於爲了保證它的數據安全性,必須限制於android系統的機子中,而且在內核中就已經對UID進行了驗證。

原理

爲了保護進程空間不被別的進程破壞或者干擾,Linux的進程是相互獨立的(進程隔離),而且一個進程空間還分爲用戶空間和內核(Kernel)空間,相當於把Kernel和上層的應用程序抽像的隔離開。這裏有兩個隔離,一個進程間是相互隔離的,二是進程內有用戶和內核的隔離

Binder框架定義了四個角色:Server,Client,ServiceManager以及Binder驅動。其中Server,Client,ServiceManager運行於用戶空間,驅動運行於內核空間。這四個角色的關係類似:Server是服務器,Client是客戶終端,ServiceManager是服務註冊中心(類似房屋中介)。
這裏寫圖片描述
要進行Client-Server之間的通信,從面向對象的角度,在Server內部有一個Binder實體,在Client內部有一個Binder對象的引用,其實就是Binder的一個代理,Client通過對Binder引用間接的操作Server內部的Binder實體,這樣就實現了通信。

但是現在的問題是會有很多個提供不同服務的Server(比如有媒體播放服務,音視頻捕獲服務等),而且會有很多Client(比如多個應用都要調用媒體播放服務),那麼我們怎麼才能夠實現正確的Client調用正確的Server呢?就好比房客怎麼才能夠租到自己想要租的房子(聯繫上房東),這個時候中介就起到重要作用,房東想要出租自己的房子就必須要到中介註冊,房客想要租房子就要去中介那裏找,同樣的道理,這裏Client就是房客,Server就是房東,ServiceManager就是房屋中介,每個Server如果要提供服務就必須要去ServiceManager那裏去註冊,ServiceManager在一張查找表中記錄一個Server的名字,對應着Server的引用。Client想要獲得Server,必須通過名字到ServiceManager取找Server的引用,獲得這個Server的binder引用,通過這個binder引用去和Server通信。

以上便是Binder的架構設計,這種架構設計在很多地方都用到過,如果大家熟悉服務器分佈式服務調用就可以很容易理解,dubbo框架其實也就是這種架構,需要一臺服務器專門提供服務管理(zookeeper),所有的服務都必須要在zookeeper中註冊,其他的節點服務要調用該服務都需要在zookeeper中取取,好的扯遠了,我們繼續來看具體client-server是怎麼實現跨進程通信的。
這就是binder驅動,它爲上層Client,Server,ServiceManager之間的通信提供了基礎。

fd = open(“/dev/binder”, O_RDWR);
mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);

這裏使用了linux的系統調用方法mmap(),這裏使用mmap創建數據接收的緩存空間。這裏必須要說一下mmap方法,mmap()分配的內存除了映射進了接收方進程裏,還映射進了內核空間,因而使用copy_from_user()將發送方的緩存數據拷貝到mmap中的內存空間時,其實接收方就可以直接獲取這塊內存空間了,這樣就只需要拷貝一次,而不需要像傳統的IPC方式那樣,需要copy_from_user將進程1的用戶空間拷貝到內核空間,再使用copy_to_user將數據從內核空間拷貝到進程2的用戶空間。

上面我們一直在說Client與Server之間的IPC,其實Server與ServiceManager也要通過Binder進行通信,因爲本身Server與ServiceManager就在不同的進程中,那麼問題來了,剛剛說Server去ServiceManager註冊,這個過程是怎麼實現的呢,其實這個通信還是binder實現的,只是ServiceManager充當了Server的角色不用註冊而已,我們註冊的的目的是爲了讓Client找到正確的Server,但是ServiceManager只有一個,我們這個時候就可以將ServiceManager創建的binder引用定死,android系統在啓動的時候就已經創建了ServiceManager的binder(通過調用BINDER_SET_CONTEXT_MGR告訴系統該進程是ServiceManager進程),而且每個Client都知道這個binder的引用代號爲0,所以每個Client就可以直接獲得這個binder引用,而不需要從其他地方獲取。

Binder的線程管理

每個Binder的Server進程會創建很多線程來處理Binder請求,可以簡單的理解爲創建了一個Binder的線程池吧(雖然實際上並不完全是這樣簡單的線程管理方式),而真正管理這些線程並不是由這個Server端來管理的,而是由Binder驅動進行管理的

一個進程的Binder線程數默認最大是16,超過的請求會被阻塞等待空閒的Binder線程。理解這一點的話,你做進程間通信時處理併發問題就會有一個底,比如使用ContentProvider時(又一個使用Binder機制的組件),你就很清楚它的CRUD(創建、檢索、更新和刪除)方法只能同時有16個線程在跑。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章