Binder---- Android 的IPC 通信機制

什麼是Binder

        在 linux 中,進程間的通訊機制有很多種,例如管道(pipe)、消息隊列(message queue)、信號(signal)、共享內存(share memory)、套接字(socket)等方式,他們都是可以實現進程間通訊。但是,在 Android 終端上的應用軟件的通信幾乎看不到這些 IPC 通信方式,取而代之的是 BinderAndroid 同時爲 Java 環境和 C/C++ 環境提供了 Binder 機制。本篇文章主要介紹 C/C++ 環境下的 Binder 機制。

        Binder是一套輕量型的 IPC 機制,它比 linux 一般的通信方式更加簡潔,消耗的內存資源更小。Binder 主要提供以下功能:

    •  用驅動程序來推進進程間的通信。
    •  通過共享內存來提高性能。
    •  爲進程請求分配每個進程的線程池。
    •  針對系統中的對象引入了引用計數和跨進程的對象引用映射。
    • 進程間同步調用。            

Binder 的通信模型

        Binder 通信是通過 linux 的Binder Driver 來實現的。Binder 的用戶空間爲每個進程維護着一個可用的線程池,線程池用於處理到來的 IPC 以及執行進程的本地消息,Binder 通信是同步的。同時,Binder 機制是基於 OpenBinder 來實現的,Android 系統的運行都將依賴 Binder 驅動。    

        Binder 通信是基於服務器(Service)與客戶端(Client)的,所以需要 Binder 通信的進程都必須創建一個 Binder 接口。系統中有一個名爲Service Manager的守護進程管理着系統中的各個服務,它負責監聽是否有其他程序向其發送請求,如果有請求就響應,如果沒有則繼續監聽等待。每個服務都要在Service Manager中註冊,而請求服務的客戶端則向Service Manager請求服務。在Android虛擬機啓動之前,系統會先啓動Service Manager進程,Service Manager就會打開Binder驅動,並通知Binder Kernel驅動程序,這個進程將作爲System Service Manager,然後該進程將進入一個循環,等待處理來自其他進程的數據。因此,我們也可以將Binder的實現大致分爲:Binder驅動、Service Manager、Service、Client 這幾個部分。

      Binder驅動 爲上層應用程序和用戶操作提供各種操作接口;

      Service Manager主要負責管理Android系統中所有的服務,當客戶端要與服務端進行通信時,首先就會通過Service Manager來查詢和取得所需要交互的服務。當然,每個服務也都需要向Service Manager註冊自己提供的服務,以便能夠供客戶端進行查詢和獲取;

     Server 這裏的服務即上面所說的服務端,通常也是Android的系統服務,通過Service Manager可以查詢和獲取某個Server;

     client 這裏的客戶端一般是指Android系統上面的應用程序。它可以請求Server中的服務,比如Activity;

      

                        

                                                                                Binder  通信原理圖


基於Client-Server的通信方式廣泛應用於從互聯網和數據庫訪問到嵌入式手持設備內部通信等各個領域。智能手機平臺特別是Android 系統中,爲了嚮應用開發者提供豐富多樣的功能,這種通信方式更是無處不在,諸如媒體播放,視音頻頻捕獲,到各種讓手機更智能的傳感器(加速度,方位,溫度,光亮度等)都由不同的Server負責管理,應用程序只需做爲Client與這些Server建立連接便可以使用這些服務,花很少的時間和精力就能開發出令人眩目的功能。Client-Server方式的廣泛採用對進程間通信(IPC)機制是一個挑戰。目前linux支持的IPC包括傳統的管道,System V IPC,即消息隊列/共享內存/信號量,以及socket中只有socket支持Client-Server的通信方式。當然也可以在這些底層機制上架設一套協議來實現Client-Server通信,但這樣增加了系統的複雜性,在手機這種條件複雜,資源稀缺的環境下可靠性也難以保證。


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通信首先必須建立這個管道並獲得管道入口。

      與其它IPC不同,Binder使用了面向對象的思想來描述作爲訪問接入點的Binder及其在Client中的入口:Binder是一個實體位於 Server中的對象,該對象提供了一套方法用以實現對服務的請求,就象類的成員函數。遍佈於client中的入口可以看成指向這個binder對象的 ‘指針’,一旦獲得了這個‘指針’就可以調用該對象的方法訪問server。在Client看來,通過Binder‘指針’調用其提供的方法和通過指針調用其它任何本地對象的方法並無區別,儘管前者的實體位於遠端Server中,而後者實體位於本地內存中。‘指針’是C++的術語,而更通常的說法是引用,即Client通過Binder的引用訪問Server。而軟件領域另一個術語‘句柄’也可以用來表述Binder在Client中的存在方式。從通信的角度看,Client中的Binder也可以看作是Server Binder的‘代理’,在本地代表遠端Server爲Client提供服務。本文中會使用‘引用’或‘句柄’這個兩廣泛使用的術語。

      面向對象思想的引入將進程間通信轉化爲通過對某個Binder對象的引用調用該對象的方法,而其獨特之處在於Binder對象是一個可以跨進程引用的對象,它的實體位於一個進程中,而它的引用卻遍佈於系統的各個進程之中。最誘人的是,這個引用和java裏引用一樣既可以是強類型,也可以是弱類型,而且可以從一個進程傳給其它進程,讓大家都能訪問同一Server,就象將一個對象或引用賦值給另一個引用一樣。Binder模糊了進程邊界,淡化了進程間通信過程,整個系統彷彿運行於同一個面向對象的程序之中。形形色色的Binder對象以及星羅棋佈的引用彷彿粘接各個應用程序的膠水,這也是Binder 在英文裏的原意。

      當然面向對象只是針對應用程序而言,對於Binder驅動和內核其它模塊一樣使用C語言實現,沒有類和對象的概念。Binder驅動爲面向對象的進程間通信提供底層支持。


Binder  驅動

        驅動程序部分驅動程序的部分在以下的文件夾中:                

  kernel/include/linux/binder.h

  kernel/drivers/Android/binder.c

        binder驅動程序是一個miscdevice,主設備號爲10,此設備號使用動態獲得(MISC_DYNAMIC_MINOR),其設備的節點爲:/dev/binder

        在其驅動的實現過程中,主要通過binder_ioctl函數與用戶空間的進程交換數據。BINDER_WRITE_READ用來讀寫數據,數據包中有一個cmd域用於區分不同的請求。binder_thread_write函數用於發送請求或返回結果,而binder_thread_read函數則用於讀取結果。在binder_thread_write函數中調用binder_transaction函數來轉發請求並返回結果。當收到請求時,binder_transaction函數會通過對象的handle找到對象所在的進程,如果handle爲空,就認爲對象是context_mgr,把請求發給context_mgr所在的進程。請求中所有的Binder對象全部放到一個RB樹中,最後把請求放到目標進程的隊列中,等待目標進程讀取。數據的解析工作放在binder_parse()中實現;關於如何生成context_mgr,內核中提供了BINDER_SET_CONTEXT_ MGR命令來完成此項功能。


Binder 相關結構體

      1.struct binder_work;

      2.Binder 對象———struct flat_binder_object

           我們把進程之間傳遞的數據稱之爲Binder對象(Binder Object),它在對應源碼中使用flat_binder_object結構體(位於binder.h文件中)來表示

      3.struct binder_transaction_data

           用於 Binder 驅動的實現

      4.struct binder_write_read

      5.binder_proc

            binder_proc結構體用於保存調用Binder的各個進程或線程的信息,比如線程ID、進程ID、Binder狀態信息等。

      6.struct binder_node該結構體表示一個Binder節點。

      7.struct binder_thread binder_thread結構體用於存儲每一個單獨的線程的信息

      8.binder_transaction 主要用來中轉請求和返回結果,保存接收和要發送的進程信息

                                      

       其中,Binder 驅動在前面已經介紹了,它用於實現Binder的設備驅動,主要負責組織Binder的服務節點,調用Binder相關的處理線程,完成實際的Bainder傳輸等,它位於Binder結構的最底層(即Linux內核層)。Binder Adapter層是對Binder驅動的封裝,主要用於操作Binder驅動,即應用程序不必直接接觸Binder驅動程序,實現包括IPCThreadState.cpp和ProcessState.cpp,以及Parcel.cpp中的部分內容。Binder核心庫是Binder框架的核心實現,主要包括IBinder、Binder(服務器端)和BpBinder(客戶端);位最上面兩層的Binder框架和具體的客戶端/服務端都分別有Java和C++兩種實現方案,主要供應用程序使用,比如攝像頭和多媒體等,它們通過調用Binder的核心庫來實現。由於這幾個部分關係緊密,不便於單獨分析每個模塊,因此需要結合Binder的本地實現一起進行分析。


Binder的工作流程

1)客戶端首先獲得服務器端的代理對象。所謂的代理對象實際上就是在客戶端建立一個服務端的“引用”,該代理對象具有服務端的功能,使其在客戶端訪問服務端的方法就像訪問本地方法一樣。

2)客戶端通過調用服務器代理對象的方式向服務器端發送請求。

3)代理對象將用戶請求通過Binder驅動發送到服務器進程。

4)服務器進程處理用戶請求,並通過Binder驅動返回處理結果給客戶端的服務器代理對象。

5)客戶端收到服務器端的返回結果。


Binder是一種架構,這種架構提供了服務端接口、Binder驅動、客戶端接口三個模塊,和一個守護進程Service Manager。

首先來看服務端。一個Binder服務端實際上就是一個Binder類的對象,該對象一旦創建,內部就啓動一個隱藏線程。該線程接下來會接收Binder驅動發送的消息,收到消息後,會執行到Binder對象中的onTransact()函數,並按照該函數的參數執行不同的服務代碼。因此,要實現一個Binder服務,就必須重載onTransact()方法。

可以想象,重載onTransact()函數的主要內容是把onTransact()函數的參數轉換爲服務函數的參數,而onTransact()函數的參數來源是客戶端調用transact()函數時輸入的,因此,如果transact()有固定格式的輸入,那麼onTransact()就會有固定格式的輸出。

下面再看Binder驅動。任意一個服務端Binder對象被創建時,同時會在Binder驅動中創建一個mRemote對象,該對象的類型也是Binder類。客戶端要訪問遠程服務時,都是通過mRemote對象。


最後來看應用程序客戶端。客戶端要想訪問遠程服務,必須獲取遠程服務在Binder對象中對應的mRemote引用,至於如何獲取,下面幾節將要介紹。獲得該mRemote對象後,就可以調用其transact()方法,而在Binder驅動中,mRemote對象也重載了transact()方法,重載的內容主要包括以下幾項。

以線程間消息通信的模式,向服務端發送客戶端傳遞過來的參數。

掛起當前線程,當前線程正是客戶端線程,並等待服務端線程執行完指定服務函數後通知(notify)。

接收到服務端線程的通知,然後繼續執行客戶端線程,並返回到客戶端代碼區。

從這裏可以看出,對應用程序開發員來講,客戶端似乎是直接調用遠程服務對應的Binder,而事實上則是通過Binder驅動進行了中轉。即存在兩個Binder對象,一個是服務端的Binder對象,另一個則是Binder驅動中的Binder對象,所不同的是Binder驅動中的對象不會再額外產生一個線程。

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