Binder 是 Android 系統引入的一種 IPC(Inter-Process Communication)方式。Binder 在 Android 系統中扮演着十分重要的角色,到處可以見到它的身影。本文主要介紹 Binder 的一些基本概念。
在 Linux 系統中,實作程序間通信方式有很多種,比如,命名管道,消息隊列,信号量,socket 等。Android 最終選擇 Binder 作為主要的 IPC 方式(zygote 使用 socket 作為其 IPC),主要出于以下幾方面的原因:
1. Binder 能夠進行安全鑒别
傳統 IPC 方式沒有任何安全措施,接收方無法獲得對方程序可靠的 UID/PID,進而無法鑒别對方身份。Android 為每個應用程式配置設定了自己的 UID,并且身份辨別資訊由 Binder 驅動在傳輸過程中添加,是以 Binder 可以有效的鑒别對方身份。
2. Binder 傳輸效率高
socket 主要用于跨網絡的程序間通訊,相對其他 IPC 方式而言,開銷大,傳輸效率低。命名管道和消息隊列采用存儲-轉發方式,需要經過2次記憶體拷貝;而Binder隻需要拷貝一次記憶體,效率大大提升。
3. Binder 能夠很好的實作 Client-Server 架構
在上述 Linux 的程序間通信方式中,隻有 socket 是 Client-Server 架構,但是 socket 效率不夠好,尤其是需要提供同步/異步互動時。
4. 自動化 GC
Binder 中比較容易構造出一套自動化垃圾回收機制,可以良好服務于 Java 虛拟機。
Binder 通信模型涉及到四個角色,如下圖所示:

Binder 驅動
Binder 驅動是整個 Binder 機制的基石。由于 Android 基于 Linux 核心,應用程式都運作在使用者空間中,且每個應用程式程序都有獨立的位址空間。是以一般涉及到兩個應用程式之間的通信時,需要通過核心進行中轉,也就是通過 copy_from_user 函數将資料從發送方緩存器拷貝到核心緩存區,然後再通過 copy_to_user 函數将資料從核心緩存區拷貝到接收方緩存區。對 Android 而言,程序間通信時負責中轉的就是 Binder 驅動。此外,Binder 驅動還負責線程控制,以及通信安全檢查。
ServiceManager
Android 程序間通信使用 Client-Server 架構,是以 Android 系統中存在大量的 Service 元件。 ServiceManager 可以了解為 Service 上下文管理者。用于提供 Service 注冊以及檢索功能。實際上 ServiceManager 維護了一個系統中所有 Service 的全局連結清單,Client 想要獲得某個 Server 提供的服務,首先需要通過 ServiceManager 獲得 Service 的句柄。
Server
Android 使用了元件化的設計思想,将大量的核心服務放在不同的 Service 元件中。提供服務的元件和使用服務的元件可以運作在不同的程序中,通常把提供服務元件所在的程序稱為 Server。
Service 是 Server 中運作的元件,一個 Server 中可以運作多個 Service,比如 SystemServer 中包含了 SensorService, ActivityManagerService, PackageManagerService 等多個 Service。
Client
查詢和請求 Server 服務的應用程式程序。
在閱讀 Binder 通信機制相關代碼過程中,以上四個角色可以像坐标一樣能幫助我們很好的定位,需要時刻清楚目前位于哪個角色中,進而不至于迷失在浩瀚的代碼中。
Android Binder設計與實作 - 設計篇中将這四個角色的關系和網際網路類比,有一種讓人耳目一新的感覺。其中,Server 好比是伺服器,Client 好比是用戶端,ServiceManager 好比是域名伺服器(DNS),Binder 驅動好比是路由器。
Binder 各角色之間的關系,可以用下圖更好的說明。
ServiceManager 比較特殊,固定使用0号句柄值,沒有 Binder 引用對象。
上圖涉及到四個重要的概念。(參考自老羅的《Android系統源代碼情景分析》)
Binder 本體對象
Binder 本體對象是 Service 在使用者空間的存在形式,使用 BBinder 類來描述。Server 程序将一個 Binder 本地對象(Service)注冊到 ServiceManager 時,Binder 驅動會為其建立一個對應的 Binder 實體對象。Binder 本地對象會被驅動程式中的 Binder 實體對象所引用。
Binder 實體對象
Binder 實體對象是 Service 在核心空間的存在形式,使用 binder_node 結構體來描述。當 Client 程序第一次引用一個 Binder 實體對象時,Binder 驅動會為它建立一個 Binder 引用對象。Binder 實體對象會被驅動程式中的 Binder 引用對象所引用。
Binder 引用對象
Binder 引用對象也存在于核心空間,使用 binder_ref 結構體來描述。Binder 引用對象被 Client 端的 Binder 代理對象所引用。
Binder 代理對象
Binder 代理對象存在于 Client 端使用者空間,使用 BpBinder 類描述。Binder 代理對象都是通過一個句柄值和 Binder 引用對象關聯的。Binder 代理對象通過 Binder 引用對象可以找到它所對應的 Binder 實體對象,進而找到 Binder 本地對象。
了解上述概念後,可以通過以下幾個典型的通信過程來了解上圖。
1. ServiceManager 成為服務管理者。ServiceManager 是使用者空間中的一個守護程序。在應用程式啟動時,就會和 Binder 驅動進行通信,告訴 Binder 驅動它作為服務上下文管理者。在此過程中,Binder 驅動會建立 ServiceManager 對應的 Binder 實體,并将該 Binder 實體設定為全局可通路。
2. Service 注冊到 ServiceManager 中。Service 使用0号句柄向 Binder 驅動發起注冊服務請求,Binder 驅動發現是注冊服務,會建立一個對應的 Binder 實體對象和一個 Binder 引用對象。然後,Binder 驅動會将注冊請求轉發給 ServiceManager處理,ServiceManager 最終會将 Service 相關資訊儲存在 svclist 全局連結清單中,相關資訊包含兩部分:Service 對應的服務名以及 Binder 引用對象對應的句柄值。
3. Client 擷取遠端服務。Client 使用0号句柄向 Binder 驅動發起查詢服務請求,Binder 驅動将該請求轉發給 ServiceManager 處理。ServiceManager 收到查詢服務請求後,根據 Client 發送過來的服務名從 svclist 全局連結清單中找到目标 Service。接下來,ServiceManager 通過 Binder 驅動将 Service 對應的句柄值傳回給 Client,在此過程中會為 Client 建立一個對應的 Binder 引用對象。Client 獲得服務句柄值後,就可以通過建立的 Binder 引用對象找到它所對應的 Binder 實體對象,進而找到 Binder 本地對象,進而使用遠端服務。
以上是 Binder 通信機制的一些重要的基本概念,雖然忽略掉了非常多的細節,但應該可以對 Binder 有一個感性的認識。接下來會介紹 Binder 通信機制中使用到的資料結構。
參考資料:
1. Android Binder機制(一) Binder的設計和架構
2. 淺談Android系統程序間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路