Android Binder 全解析(2) -- 設計詳解

原文鏈接:http://www.woaitqs.cc/android/2016/05/26/android-binder-token.html

在上一篇文章中介紹了什麼是Binder? 爲什麼我們需要它?在這一篇文章中,將通過類比的思路來介紹 Binder 的設計原理,作爲上一篇文章的補充。這篇文章只是從設計的概念出發進行理解,不設計太多的代碼細節,如果想對具體實現感興趣,可以參考老羅的文章。Android進程間通信(IPC)機制Binder簡要介紹和學習計劃。希望通過這篇文章,能夠幫助大家理解整個 Binder 運作機制。

在這篇文章結束後,將介紹 Binder Token的 妙用,和 Death Notification 的使用場景,感謝大家持續關注。

我們是怎麼上網的?

在互聯的時代,將各種設備連接上互聯網,是我們使用一個新設備的第一件事情,網絡無處不在。在這個互聯的社會,是如何標記彼此呢?如何保證我想要上京東的時候,打開的網頁不是淘寶?保證每一個網絡設備能夠被獨立標誌的功臣就是 Internet Protocol address。 IP 地址就是用於在互聯網通信過程中,分配給設備的唯一標示,其他設備可以通過這一唯一標示訪問當前設備。下面顯示的就是 google.com.hk 的 IP 地址。

$ ping www.google.com.hk
PING www.google.com.hk (74.125.203.94): 56 data bytes
64 bytes from 74.125.203.94: icmp_seq=0 ttl=43 time=53.749 ms
64 bytes from 74.125.203.94: icmp_seq=1 ttl=43 time=55.464 ms
64 bytes from 74.125.203.94: icmp_seq=2 ttl=43 time=56.658 ms
64 bytes from 74.125.203.94: icmp_seq=3 ttl=43 time=56.441 ms
64 bytes from 74.125.203.94: icmp_seq=4 ttl=43 time=55.051 ms
64 bytes from 74.125.203.94: icmp_seq=5 ttl=43 time=54.940 ms

當我們需要訪問某個網站時,可以通過某個 IP 直接來訪問,例如直接在瀏覽器上訪問 74.125.203.94 就可以訪問 Google,但這個 IP 地址就是一組數字,非常不利於記憶,所有 IP 地址都是數字,顯然不方便我們使用。那麼如何來解決這個問題?答案也是我們耳熟能詳的 DNS(Domain Name System)。

域名服務器 提供的功能提供了域名翻譯的問題,每個網站都可以有屬於自己的域名,例如 woaitqs.github.iomp.weixin.qq.com 等等。通常我們只需要在服務端輸入這個域名,就能訪問到對應的 IP 地址。這個過程發生了什麼?實際上域名解析服務器,給我們提供了翻譯功能,也就是說域名服務器幫助我們維護着 域名 和 對應 IP 的Map。當我們輸入一個域名時,服務器會自動將這個域名解析成對應的地址,並訪問相應的服務,我們只需要記住相應的域名就可以。

使用域名服務器時,我們需要將我們的服務在域名提供商哪裏註冊進去,常見的域名提供商是 萬網西部數碼,在註冊過後,自定義域名的實際訪問地址,就解決了通過域名訪問我們服務的問題。

從域名到目的地並非簡單的映射就可以完成,爲了更好的擴展性和高效性,需要有完善的算法和設備支持,從這裏可以看到更詳細介紹,將這個從源地址傳輸到目的地址的活動成爲路由。

通常情況下,我們訪問其他域名,就是將域名對應的地址作爲 Server,而我們本身作爲 Client,也就是我們常說的 Client\Server 架構。每一次訪問相應網址的時候,都是 Client 向 Server 發送請求,並等待 Server 的回覆。

Binder 架構設計

前面粗略地介紹了 我們是怎麼上網的? 這個看上去和本文沒有關係的內容,花那麼多時間進行介紹,是爲了方便大家理解 Binder 架構。兩者之間存在着異曲同工之妙。

Binder 被設計出來是解決 Android IPC(進程間通信) 問題的,爲什麼選用 Binder,可以參看我的上一篇文章 Android Binder 全解析(一)。Binder 將兩個進程間交互的理解爲 Client 向 Server 進行通信,在接下來的內容中,會將兩者結合起來進行類比。

先看一張圖
binder總體架構

如上圖所示,Binder 架構分爲 Client、Server、Service Manager 和 Binder Driver。

  • Client: 服務調用者,一般就是我們應用開發者,通過調用諸如List<PackageInfo> packs = getActivity().getPackageManager().getInstalledPackages(0); 這樣的代碼,來向 ServerManager 請求 Package 服務。
  • Server: 服務提供者,這裏面會有許多我們常用的服務,例如 ActivityService 、 WindowMananger, 這些系統服務提供的功能,是的我們能夠使用 Wifi,Display等等設備,從而完成我們的需求。
  • Service Manager: 這裏是類似於前文中的DNS,絕大多數的服務都是通過 Service Manager來獲取,通過這個 DNS 來屏蔽掉 對其他Server的直接操作。
  • Binder Driver: 底層的支持邏輯,在這裏承擔路由的工作,不論風雨,使命必達,即使對面的server掛掉了,也會給你相應的死亡通知單 (Death Notification)

總結起來說,應用程序(Client) 首先向 Service Manager 發送請求 WindowManager 的服務,Service Manager 查看已經註冊在裏面的服務的列表,找到相應的服務後,通過 Binder kernel 將其中的 Binder 對象返回給客戶端,從而完成對服務的請求。

Binder Driver 是怎樣充當路由角色的?

對於有網絡編程經驗的人來說,Socket 是很常用的概念。在Linux系統中,一切都被認爲是文件,網絡流也是文件,同樣 Socket 也是文件,遵循着 open - write / read - close 的模式,Binder Framework在設計的時候,也同樣設計了類似的概念。

而在 Binder Framework 中 Binder 充當了 Socket 的角色,在不同的進程裏面穿梭,提供了通信的基礎。對Binder而言,Binder可以看成Server提供的實現某個特定服務的訪問接入點, Client通過這個『地址』向Server發送請求來使用該服務;對Client而言,Binder可以看成是通向Server的管道入口,要想和某個Server通信首先必須建立這個管道並獲得管道入口。我們知道如果要訪問一個對象的話,需要拿到這個對象的引用地址,我們可以這麼認爲 Binder 就是遠程對象的一個地址,通過這個 Binder 就能輕鬆地拿到遠程對象的控制權,也可以說 Binder 是句柄,可能符合現在的場景。

而讓 Binder 起到上訴神奇作用的就是 Binder Driver。Binder Driver 在這裏的作用就是前面提及的路由器,它工作在內核態,通過一系列 open() , mmap(), ioctl() , poll() 等操作,指定了一系列的協議,實現了 Binder 在不同進程之間的傳遞工作,這裏就不再詳細闡述了,有興趣的同學可以自行查看相關文檔。

Service Manager 怎麼當DNS的?

根據前文的描述,Service Manager是將相應的服務名字轉換成具體的引用,也就是說使得 client 能夠通過 bidner 名字來從 Server 中拿到對 binder 實體的引用。這裏唯一需要特別說明的地方在於,Service Manager 的特殊性。我們知道 Service Manager 是一個進程,其他 Server 也是另一個進程,他們之間是如何進行通信的了?在沒有其他中間服務進程的參與下,Service Manager 與 其他進程如何憑空通信?

這就是先有雞,還是先有蛋的問題。答案是先有雞,也就是說 Service Manager 首先就被創建了,並被賦予了一個特殊的句柄,這個句柄就是 0 。換而言之,其他 Server 進程都可以通過這個 0句柄 與 Service Manager 進行通信,在整個系統啓動時,其他 Server 進程都向這個 0句柄 進行註冊,從而使得客戶端進程在需要調用服務時,能夠通過這個 Service Manager 查詢到相應的服務進程。

binder framework 工作原理

Proxy 的由來

Binder Framework 作爲一個底層框架,使用的場景相當的廣,但也使得不太適合面向對象開發。爲了滿足這樣的需求,Android 的工程師採用了代理模式 Proxy 來解決這個問題。

代理模式 UML

首先定義一套相同的接口,服務端 和 客戶端分別使用這套接口,服務端具體實現了這套接口的相應邏輯,而客戶端也實現了這套接口,不過接口裏面的具體實現是調用相應的遠程服務接口,將函數參數打包,通過Binder向Server發送申請並等待返回值。

做爲Proxy設計模式的基礎,首先定義一個抽象接口類封裝Server所有功能,其中包含一系列純虛函數留待Server和Proxy各自實現。由於這些函數需要跨進程調用,須爲其一一編號,從而Server可以根據收到的編號決定調用哪個函數。而這裏的 Binder 具有唯一的標示性,在後面的文章中,再來說明 Android 系統如何使用這一特性。

參考文獻

  1. http://blog.csdn.net/universus/article/details/6211589
  2. http://blog.csdn.net/luoshengyang/article/details/6618363
  3. http://www.cnblogs.com/innost/archive/2011/01/09/1931456.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章