Android Binder 全解析(1) -- 概述
什麼是Binder? 為什麼我們需要它?
在提及Binder之前,我們先來看看Android的設計。在Linux系統裡面,程序之間是相互隔離的,也就是說程序之間的各個資料是互相獨立,互不影響,而如果一個程序崩潰了,也不會影響到另一個程序。這樣的前提下將互相不影響的系統功能分拆到不同的程序裡面去,有助於提升系統的穩定性,畢竟我們都不想自己的應用程序崩潰會導致整個手機系統的崩潰。而Android是基於Linux系統進行開發的,也充分利用的程序隔離這一特性。
這些Android的系統程序,從System Server 到 SurfaceFlinger,qcks,各個程序各司其職,支撐起整個Android系統。而我們進行的Android開發也是和這些系統程序打交道,通過他們提供的服務,架構起我們的App程式。那麼有了這些程序之後,問題緊接著而來,我們怎麼和這些程序合作了?答案就是IPC
Linux System 在IPC中,做了很多工作,提供了不少程序間通訊的方式,下面羅列了幾種比較常見的方式。
- Signals 訊號量
- Pipes 管道
- Socket 套接字
- Message Queue 訊息佇列
- Shared Memory 共享記憶體
按照複用的角度上看,既然有這麼多”輪子”後,就應該合理利用這些”輪子”,從而方便地呼叫系統服務。然而事實並沒有這麼簡單,Android系統作為嵌入式的移動作業系統,通訊條件相對更加苛責一些,苛責的地方提現在:
- 拮据的記憶體,移動裝置上的記憶體情況不同於PC平臺,記憶體受限,因而需要有合適的機制來保證對空閒程序的回收
- Android 不支援System V IPCs
- 安全性問題顯得更為突出,移動平臺特有的許可權問題
- 需要Death Notification(程序終止的通知)的支援
由於前面提及的特殊性,先前的輪子已經不能滿足所有的需求了,因而就有了今天的主角 Binder。Binder 是一個基於OpenBinder開發,Google在其中進行了相應的改造和優化,在面向物件系統裡面的IPC/元件,適配了相關特性,並致力於建立具有擴充套件性、穩定、靈活的系統。
在這一小節結束的時候,來看一看Binder在Android系統中的使用場景,也就是圖中的IPC模組。
Binder Framework 願景
既然需要重新造輪子,那麼接下來讓我們沿著設計者的思路,來看看如何一步一步滿足前面提及的特殊需求。Binder在本質上需要解決的問題是讓兩個不同的程序之間能夠互相呼叫的問題,所以從開發者的視角來看,應該就簡單地如下圖:
同時從效能的角度上出發,希望提供服務呼叫的程式能夠支援併發,這樣使得能夠儘可能地響應多個程式的IPC請求,由此得出的實際執行圖是下面這個樣子的。
Binder Framework 實現細節
有了前面這些願景後,再來看看Binder Framework的一些實現細節,如何走到這一步的,當然這是非常泛的細節,作為常識瞭解一下。
如何跨程序呼叫
那麼如何使得呼叫者能像上述一樣簡單地呼叫遠端方法?畢竟兩者存在於不同的程序空間裡面。那麼可以引入一個黑盒模組,用這個黑盒模組來幫我們完成其中的細節,這個模組也被稱為Binder Driver。方法的跨程序呼叫受到了 Linux 程序隔離的限制,而解決方案就是將其置於所有程序都能共享的區域 – Kernel,而 Binder Driver 提供的功能也就是讓各程序使用核心空間,將程序中的地址和Kernel中的地址對映起來,其中Linux ioctl 函式實現了從使用者空間轉移到核心空間的功能。在 Binder Driver 的支援下,就能實現跨程序呼叫。
Client / Server 架構
在設計的時候,Binder Framework 互動模型採用的是客戶端/伺服器模型。客戶端需要呼叫遠端服務的內容時, 會初始化一個連線,並等待伺服器的返回,同時會block住自己。Binder Framework在客戶端這邊實現了一個代理,而在服務端,通過執行緒池的方式來響應請求。在如下圖所示,A程序就是客戶端,並且通過Proxy來完成對Binder Driver核心的互動。Process B是系統服務程序,在這個程序裡面維護著多個Binder Thread,直到達到設定的執行緒上限。Proxy物件通過和Binder Driver進行互動,從而使得Binder Driver將資訊傳遞到目標物件。從Android開發者的角度出發,Binder Framework提供的最方便的改進就是能像呼叫本地方法一樣呼叫遠端方法或物件。客戶端的程序呼叫會在Server程序返回之前一直處於block的狀況。在這種機制下,客戶端就不必提供一個單獨的執行緒模型和回撥機制。(同步轉非同步簡單,而非同步變同步則很困難)
傳遞的資料格式
在實現跨程序呼叫的時候,涉及到引數和命令的傳遞,得有一個合適的資料結構來表達需要遠端執行的東西。
Target是指目標binder,Cookie這涵蓋著一些內部資訊,sender Id則包含了安全相關的資訊,data則包含著一些資料的陣列。每個陣列的Entry是由相關的命令和引數組成的,這部分引數將傳遞給目標binder。
而這裡面的Sender Id 則非常的重要,不僅可以起到唯一標示Binder的作用,還可以在跨程序的地方作為標記的作用,在接下來的文章裡再詳細說明。
Service Manager
我們接觸的服務很多,從Display到Location,從Audio到Wifi,如果我們和每一個服務都通過前面描述的方式進行互動,即便通過 Proxy 的方式也是非常的繁瑣。而且在呼叫每個系統服務的時候,必須知道對應的系統服務的地址,而系統服務的地址出於安全性的考慮也不應該暴露出來。那麼如何方便我們進行系統服務呼叫了?
Service Manager 就是來幫助我們解決這個問題。這是Binder Framework的一個特殊節點,也是第一個起點。其作為一個命令服務,起到了DNS的作用,使得可以通過名字的方式來查詢相應的Binder介面。這很重要,因為客戶端不應該知道遠端服務的呼叫地址,如果知道了這勢必會很不安全。每一個Binder需要將自己的名字和Binder Token交給Service Manager,客戶端只需要知道服務的名字就可以。