借助 AIDL 理解 Android Binder 機制——Binder 來龍去脈

AIDL 是 Android Interface Definition Language(Android 接口定義語言)的縮寫,它是 Android 進程間通信的接口語言。由于 Android 系統的內核是 Linux,它采用了進程隔離機制,使得不同的應用程序運行在不同的進程當中,有時候兩個應用之間需要傳遞或者共享某些數據,就需要進行進程間的通信訊。

Android 進程間通信的方式有很多種,比如 Messenger、文件(SharePreference)、AIDL、Socket 和 Content Provider 等,它們當中 Messenge、AIDL 和 Content Provider 的底層都是依賴于 Binder 機制去實現的。除此之外,Android 四大組件的啟動和通信的核心過程也是通過 Binder 機制去實現的,這里,我們借助 AIDL 來了解 Binder 的實現機制。

在了解 AIDL 機制和用法之前,首先要了解幾個概念,這對后續的深入理解有較大的幫助。

進程隔離

以下內容來自維基百科

進程隔離是為保護操作系統中進程互不干擾而設計的一組不同硬件和軟件的技術。這個技術是為了避免進程 A 寫入進程 B 的情況發生。 進程的隔離實現,使用了虛擬地址空間。進程 A 的虛擬地址和進程B的虛擬地址不同,這樣就防止進程 A 將數據信息寫入進程 B。

Linux IPC 原理

由于 Linux 采用了虛擬地址空間技術,操作系統在邏輯上將虛擬內存分為用戶空間(User Space)內核空間(Kernel Space),普通應用程序運行在用戶空間,系統內核運行在內核空間,為了控制應用程序的訪問范圍、保證系統安全,用戶空間只能通過系統調用的方式去訪問內核空間。當進程執行系統調用而陷入內核代碼的時候,該進程則進入了內核態,相比之下,進程在用戶空間執行自己的代碼的時候,則是處于用戶態

由于進程 A 和進程 B 的虛擬地址不同,因此它們之間是相互透明的,都以為自己獨享了系統的資源,當然也不能直接跟對方交互。但是,有些情況下有些進程難免會需要跟其他進程進行交互,這個交互過程就叫 IPC(Inter-Process Communication,進程間通信)。IPC 的實質就是數據的交互,因此我們這里將進行 IPC 過程中的通信調用方和被調用放分別稱為數據發送方和數據接收方,IPC 通信的過程如下:

  1. 數據發送方進程將數據放在內存緩存區,通過系統調用陷入內核態
  2. 內核程序在內核空間開辟一塊內核緩存區,通過 copy_from_user 函數將數據從數據發送方用戶空間的內存緩存區拷貝到內核空間的內核緩存區中
  3. 數據接收方進程在自己的用戶空間開辟一塊內存緩存區
  4. 內核程序將內核緩存區中通過 copy_to_user 函數將數據拷貝到數據接收方進程的內存緩存區
Linux IPC

通過以上過程,一次 IPC 就完成了,但是這種傳統的 IPC 機制有兩個問題:

  • 性能比較低:整個過程數據的傳遞需要經歷發送方內存緩存區——內核緩存區——接收方內存緩存區的過程
  • 接收方進程事先不知道需要開辟多大的內存用于存放數據,因此需要開辟盡可能大的空間或者事先調用 API 來解決這個問題,這兩種方式不是浪費空間就是浪費時間。

Binder IPC 原理

為了克服 Linux 傳統的 IPC 機制中的不足之處,Android 系統引入了 Binder 機制,從字面上看 Binder 是膠水的意思,在這里,Binder 的職責是在不同的進程之間扮演一個橋梁的角色,讓它們之間能夠相互通信。從上一小節內容可以了解到,進程間的通訊少不了 Linux 內核的支持,而 Binder 并不屬于內核的一部分,但是,得益于 Linux 的 LKM(Loadable Kernel Module) 機制:

模塊是具有獨立功能的程序,它可以被單獨編譯,但不能獨立運行。它在運行時被鏈接到內核作為內核的一部分在內核空間運行

因此,Binder 作為這種模塊存在于內核之中,也稱為 Binder 驅動。回顧上一小節的內容,傳統 Linux IPC 的過程需要經歷兩次數據拷貝,Binder 借助 Linux 的另一個特性,只用一次數據拷貝,就能實現 IPC 過程,這就是內存映射

Binder IPC 機制中涉及到的內存映射通過 mmap() 來實現,mmap() 是操作系統中一種內存映射的方法。內存映射簡單的講就是將用戶空間的一塊內存區域映射到內核空間,映射關系建立后,用戶對這塊內存區域的修改可以直接反應到內核空間;反之內核空間對這段區域的修改也能直接反應到用戶空間。

內存映射能減少數據拷貝次數,實現用戶空間和內核空間的高效互動。兩個空間各自的修改能直接反映在映射的內存區域,從而被對方空間及時感知。也正因為如此,內存映射能夠提供對進程間通信的支持。

Binder IPC 通信過程如下:

  1. Binder 驅動在內核空間創建一個數據接收緩存區
  2. 然后在內核空間開辟一塊內存緩存區并與數據接收緩存區建立映射關系,同時,建立數據接收緩存區數據接收方的內存緩存區的映射關系
  3. 數據發送方通過系統調用 copy_from_user 函數將數據從內存緩存區拷貝到內核緩存區,由于內核緩存區通過數據接收緩存區跟數據接收方的內存緩存區存在間接的映射關系,相當于將數據直接拷貝到了接收方的用戶空間,這樣便完成了一次 IPC 的過程。
Android IPC

Binder 通信模型和通信過程

在進行 Binder IPC 的時候,實際情況比上面介紹的要復雜,Binder 通訊模型是基于 C/S 架構的,通信調用方進程稱為 Client 進程,被調用方稱為 Server 進程,除此之外還需要 ServiceManager 和 Binder 驅動的參與,它們都是通過 open/mmap/iotl 等系統調用來訪問設備文件 dev/binder 來實現 IPC 過程的。

Binder IPC module

其中,Client、Server 和 ServiceManager 運行在用戶空間,Binder Driver 運行在內核空間,Client 和 Server 需由用戶自己實現,ServiceManager 和 Binder Driver 則由系統提供。

Android Binder 設計與實現 文章中對 Client 和 Server 等角色有詳細的描述:

Binder 驅動:
Binder 驅動就如同路由器一樣,是整個通信的核心;驅動負責進程之間 Binder 通信的建立,Binder 在進程之間的傳遞,Binder 引用計數管理,數據包在進程之間的傳遞和交互等一系列底層支持。

ServiceManager 與實名 Binder:
ServiceManager 和 DNS 類似,作用是將字符形式的 Binder 名字轉化成 Client 中對該 Binder 的引用,使得 Client 能夠通過 Binder 的名字獲得對 Binder 實體的引用。注冊了名字的 Binder 叫實名 Binder,就像網站一樣除了除了有 IP 地址意外還有自己的網址。Server 創建了 Binder,并為它起一個字符形式,可讀易記得名字,將這個 Binder 實體連同名字一起以數據包的形式通過 Binder 驅動發送給 ServiceManager ,通知 ServiceManager 注冊一個名為“張三”的 Binder,它位于某個 Server 中。驅動為這個穿越進程邊界的 Binder 創建位于內核中的實體節點以及 ServiceManager 對實體的引用,將名字以及新建的引用打包傳給 ServiceManager。ServiceManger 收到數據后從中取出名字和引用填入查找表。

細心的讀者可能會發現,ServierManager 是一個進程,Server 是另一個進程,Server 向 ServiceManager 中注冊 Binder 必然涉及到進程間通信。當前實現進程間通信又要用到進程間通信,這就好像蛋可以孵出雞的前提卻是要先找只雞下蛋!Binder 的實現比較巧妙,就是預先創造一只雞來下蛋。ServiceManager 和其他進程同樣采用 Bidner 通信,ServiceManager 是 Server 端,有自己的 Binder 實體,其他進程都是 Client,需要通過這個 Binder 的引用來實現 Binder 的注冊,查詢和獲取。ServiceManager 提供的 Binder 比較特殊,它沒有名字也不需要注冊。當一個進程使用 BINDERSETCONTEXT_MGR 命令將自己注冊成 ServiceManager 時 Binder 驅動會自動為它創建 Binder 實體(這就是那只預先造好的那只雞)。其次這個 Binder 實體的引用在所有 Client 中都固定為 0 而無需通過其它手段獲得。也就是說,一個 Server 想要向 ServiceManager 注冊自己的 Binder 就必須通過這個 0 號引用和 ServiceManager 的 Binder 通信。類比互聯網,0 號引用就好比是域名服務器的地址,你必須預先動態或者手工配置好。要注意的是,這里說的 Client 是相對于 ServiceManager 而言的,一個進程或者應用程序可能是提供服務的 Server,但對于 ServiceManager 來說它仍然是個 Client。

Client 獲得實名 Binder 的引用:
Server 向 ServiceManager 中注冊了 Binder 以后, Client 就能通過名字獲得 Binder 的引用了。Client 也利用保留的 0 號引用向 ServiceManager 請求訪問某個 Binder: 我申請訪問名字叫張三的 Binder 引用。ServiceManager 收到這個請求后從請求數據包中取出 Binder 名稱,在查找表里找到對應的條目,取出對應的 Binder 引用作為回復發送給發起請求的 Client。從面向對象的角度看,Server 中的 Binder 實體現在有兩個引用:一個位于 ServiceManager 中,一個位于發起請求的 Client 中。如果接下來有更多的 Client 請求該 Binder,系統中就會有更多的引用指向該 Binder ,就像 Java 中一個對象有多個引用一樣。

因此 Binder IPC 過程可以總結成以下步驟:

  1. 某個進程使用 BINDER_SET_CONTEXT_MGR 命令通過 Binder 驅動將自己注冊成 ServiceManager,負責管理所有的 Service
  2. 各個 Server 通過 Binder 驅動向 ServiceManager 注冊 Binder 實體,表明自己可以對外提供服務,這時 Binder 驅動會為這個 Binder 創建位于內核中的實體節點以及 ServiceManager 對該節點的引用,并將名字和該引用打包給 ServiceManager,ServiceManager 接收到數據包后將數據包中的名字和引用填入查找表中
  3. Client 通過上面 Server 的名字在 Binder 驅動的幫助下從 ServiceManager 中獲取到該 Server 對應的 Binder 引用對象,由于該引用對象同樣具有 Server 的能力,因此 Client 可以通過這個引用與真實的 Server 進行交互

還是universus 老師的圖:

Binder Role

總結

進程隔離雖然使操作系統的安全性和應用程序的穩定性得到了提升,但同時也給 IPC 帶來了一定的難度,Android 系統巧妙地應用了 Binder 機制,使得系統得于在存儲空間和硬件性能等有限的移動設備上能夠流暢地運行。關于 Binder 在應用層的使用和分析,請看下一篇文章內容:借助 AIDL 理解 Android Binder 機制——AIDL 的使用和原理分析

參考文章

寫給 Android 應用工程師的 Binder 原理剖析

Binder學習指南

如果你對文章內容有疑問或者有不同意見,歡迎留言,我們一同探討。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容