1. 程式人生 > >Android Binder 全解析(1) -- 概述

Android Binder 全解析(1) -- 概述

摘要 如果各位玩過《爐石傳說》,那麼可能對法師的職業卡「不穩定的傳送門」很有印象,特別是沒有歐洲玩家,經常能夠拿到其他職業的強力單卡。Android 也提供了傳送門,讓我們可以像使用本地方法一樣,呼叫其他程序的方法,他有一個響亮的名字,Binder! Binder 在 Android 是如此的重要,承當起整個Android的通訊任務,作為優秀的Android工程師有什麼理由不瞭解了?在接下來的文章中,會陸陸續續講解Android Binder,希望大家持續關注。 本文是 Android 系統學習系列文章中的第二章節,在前面一些細節概念的鋪墊下,大體上知道 Binder Framework 是怎麼運作的,在這篇文章中,將詳細說明下 Binder Framework 的具體實現,這一套機制如何盤活整個 Android 系統。對此係列感興趣的同學,可以收藏這個連結 Android 系統學習
,也可以點選 RSS訂閱 進行訂閱。

什麼是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 存在的地方

Binder Framework 願景

既然需要重新造輪子,那麼接下來讓我們沿著設計者的思路,來看看如何一步一步滿足前面提及的特殊需求。Binder在本質上需要解決的問題是讓兩個不同的程序之間能夠互相呼叫的問題,所以從開發者的視角來看,應該就簡單地如下圖:

binder-user.png

同時從效能的角度上出發,希望提供服務呼叫的程式能夠支援併發,這樣使得能夠儘可能地響應多個程式的IPC請求,由此得出的實際執行圖是下面這個樣子的。

biner-multi-thread.png

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的狀況。在這種機制下,客戶端就不必提供一個單獨的執行緒模型和回撥機制。(同步轉非同步簡單,而非同步變同步則很困難)

binder-proxy.png

傳遞的資料格式

在實現跨程序呼叫的時候,涉及到引數和命令的傳遞,得有一個合適的資料結構來表達需要遠端執行的東西。binder-data.png

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,客戶端只需要知道服務的名字就可以。

binder-mananger.png

參考文獻