Binder通訊機制
Binder學習總結
一、Binder概述
binder是Android中程式間通訊的機制。在Android framework層,binder是servicemanager連線各種manager和相應的managerservice的橋樑。對於Android應用層角度來說,binder是客戶端和服務端進行通訊的媒介。
二、Linux系統中的IPC原理
Android系統是基於linux核心的,瞭解linux ipc相關的概念有助於我們瞭解binder機制。
2.1基本知識介紹
2.1.1 程式隔離
在linux作業系統中,程式擁有自己的記憶體空間,各個程式的記憶體空間是互相隔離的,兩個程式要進行資料互動必須採用程式間的通訊機制即ipc。
2.1.1 程式空間劃分以及系統呼叫
在Android系統中,採用的是虛擬記憶體管理技術,每個程式都有各自互不干涉的程式地址空間,該空間是虛擬空間。程式地址空間被分為兩個部分,分別是使用者空間和核心空間,使用者程式通常情況下只能訪問到使用者空間的虛擬地址,只有使用者程式進行系統呼叫等時刻可以訪問到核心空間。每當程式進行切換時,使用者空間會跟著變換,而核心空間不會隨著程式改變。
系統呼叫時使用者程式訪問核心空間的方式,其保證了系統資源的訪問都是在核心的控制下進行的,避免了使用者程式對系統資源的越權訪問,提升了系統的安全性和穩定性。進行執行使用者空間的程式碼時處於
使用者態,當進行執行系統呼叫時稱程式處於
核心態。
2.2 linux ipc原理
2.2.1 linux程式間通訊的方式
pipe管道及FIFO有名管道
管道是linux支援的最初的ipc形式之一,具有以下特點:
- 管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道
- 只能用於父子程式或者兄弟程式之間(具有親緣關係的程式)
- 單獨構成一種獨立的檔案系統:管道對於管道兩端的程式而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,而是自立門戶,單獨構成一種檔案系統,並且只存在與記憶體中
- 資料的讀出和寫入:一個程式向管道中寫的內容被管道另一端的程式讀出。寫入的內容每次都新增在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出資料。
- 管道的緩衝區是有限的(管道制存在於記憶體中,在管道建立時,為緩衝區分配一個頁面大小)
- 管道所傳送的是無格式位元組流,這就要求管道的讀出方和寫入方必須事先約定好資料的格式,比如多少位元組算作一個訊息(或命令、或記錄)等等
管道應用的一個重大限制是它沒有名字,因此,只能用於具有親緣關係的程式間通訊,在有名管道(named pipe或FIFO)提出後,該限制得到了克服。FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的檔案形式存在於檔案系統中
訊號
可參考文章:
http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html
http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html
訊息佇列
訊息佇列就是一個訊息的連結串列。可以把訊息看作一個記錄,具有特定的格式以及特定的優先順序。對訊息佇列有寫許可權的程式可以向中按照一定的規則新增新訊息;對訊息佇列有讀許可權的程式則可以從訊息佇列中讀走訊息。訊息佇列與管道以及有名管道相比,具有更大的靈活性,首先,它提供有格式位元組流,有利於減少開發人員的工作量;其次,訊息具有型別,在實際應用中,可作為優先順序使用。這兩點是管道以及有名管道所不能比的。同樣,訊息佇列可以在幾個程式間複用,而不管這幾個程式是否具有親緣關係,這一點與有名管道很相似;但訊息佇列是隨核心持續的,與有名管道(隨程式持續)相比,生命力更強,應用空間更大。
共享記憶體
使得多個程式可以訪問同一塊記憶體空間,是最快的可用IPC形式。是針對其他通訊機制執行效率較低而設計的。往往與其它通訊機制,如訊號量結合使用,來達到程式間的同步及互斥。採用共享記憶體通訊的一個顯而易見的好處是效率高,因為程式可以直接讀寫記憶體,而不需要任何資料的複製。對於像管道和訊息佇列等通訊方式,則需要在核心和使用者空間進行四次的資料複製,而共享記憶體則只複製兩次資料[1]:一次從輸入檔案到共享記憶體區,另一次從共享記憶體區到輸出檔案。實際上,程式之間在共享記憶體時,並不總是讀寫少量資料後就解除對映,有新的通訊時,再重新建立共享記憶體區域。而是保持共享區域,直到通訊完畢為止,這樣,資料內容一直儲存在共享記憶體中,並沒有寫回檔案。共享記憶體中的內容往往是在解除對映時才寫回檔案的。因此,採用共享記憶體的通訊方式效率是非常高的。
Linux的2.2.x核心支援多種共享記憶體方式,如mmap()系統呼叫,Posix共享記憶體,以及系統V共享記憶體。mmap()系統呼叫使得程式之間透過對映同一個普通檔案實現共享記憶體。普通檔案被對映到程式地址空間後,程式可以向訪問普通記憶體一樣對檔案進行訪問,不必再呼叫read(),write()等操作
socket
更為一般的程式間通訊機制,可用於不同機器之間的程式間通訊
這些傳統的 IPC 通訊方式有兩個問題:
- 效能低下,一次資料傳遞需要經歷:記憶體快取區 --> 核心快取區 --> 記憶體快取區,需要 2 次資料複製;
- 接收資料的快取區由資料接收程式提供,但是接收程式並不知道需要多大的空間來存放將要傳遞過來的資料,因此只能開闢儘可能大的記憶體空間或者先呼叫 API 接收訊息頭來獲取訊息體的大小,這兩種做法不是浪費空間就是浪費時間。
三、Binder 跨程式通訊原理
3.1動態核心可載入模組
跨程式通訊需要核心空間來實現,Linux的
動態核心可載入模組的機制(Loadable Kernel Module,LKM),binder驅動模組是具有獨立功能的程式,可以被單獨編譯,但是不能獨立執行。其在執行的時候被連結到核心作為核心的一部分執行。Android系統透過動態新增一個binder模型執行在核心空間,使用者程式之間透過這個binder核心模組作為橋樑實現通訊。
binder機制是透過記憶體對映即mmap() 來實現,記憶體對映簡單的講就是將使用者空間的一塊記憶體區域對映到核心空間。對映關係建立後,使用者對這塊記憶體區域的修改可以直接反應到核心空間;反之核心空間對這段區域的修改也能直接反應到使用者空間。記憶體對映能減少資料複製次數,實現使用者空間和核心空間的高效互動。
3.1Binder IPC 實現原理
Binder IPC 正是基於記憶體對映(mmap)來實現的,但是 mmap() 通常是用在有物理介質的檔案系統上的。Binder 驅動使用 mmap() 是用來在核心空間建立資料接收的快取空間。
一次完整的 Binder IPC 通訊過程通常是這樣:
- 首先 Binder 驅動在核心空間建立一個資料接收快取區。
- 接著在核心空間開闢一塊核心快取區,建立核心快取區和核心中資料接收快取區之間的對映關係,以及核心中快取區和接收程式使用者空間地址的對映關係;
- 傳送方程式透過系統呼叫 copy_from_user() 將資料 copy 到核心中的核心快取區,由於核心快取區和接收程式的使用者空間存在記憶體對映,因此也就相當於把資料傳送到了接收程式的使用者空間,這樣便完成了一次程式間的通訊。
四、Binder 通訊模型
一次完整的程式間通訊必然至少包含兩個程式,通常我們稱通訊的雙方分別為客戶端程式(Client)和服務端程式(Server)
4.1.Client/Server/ServiceManager/驅動
Binder 是基於 C/S 架構的。由一系列的元件組成,包括 Client、Server、ServiceManager、Binder 驅動。其中 Client、Server、Service Manager 執行在使用者空間,Binder 驅動執行在核心空間。其中 Service Manager 和 Binder 驅動由系統提供,而 Client、Server 由應用程式來實現。
Client、Server 和 ServiceManager 均是透過系統呼叫 open、mmap 和 ioctl 來訪問裝置檔案 /dev/binder,從而實現與 Binder 驅動的互動來間接的實現跨程式通訊
4.1.1Binder 驅動
Binder 驅動就如同路由器一樣,是整個通訊的核心;驅動負責程式之間 Binder 通訊的建立,Binder 在程式之間的傳遞,Binder 引用計數管理,資料包在程式之間的傳遞和互動等一系列底層支援
4.1.2 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 必然涉及到程式間通訊。當前實現程式間通訊又要用到程式間通訊
4.1.3 Client 獲得實名 Binder 的引用
Server 向 ServiceManager 中註冊了 Binder 以後, Client 就能透過名字獲得 Binder 的引用了。Client 也利用保留的 0 號引用向 ServiceManager 請求訪問某個 Binder: 我申請訪問名字叫張三的 Binder 引用。ServiceManager 收到這個請求後從請求資料包中取出 Binder 名稱,在查詢表裡找到對應的條目,取出對應的 Binder 引用作為回覆傳送給發起請求的 Client。從物件導向的角度看,Server 中的 Binder 實體現在有兩個引用:一個位於 ServiceManager 中,一個位於發起請求的 Client 中。如果接下來有更多的 Client 請求該 Binder,系統中就會有更多的引用指向該 Binder ,就像 Java 中一個物件有多個引用一樣。
4.2 Binder 通訊過程
- 首先,一個程式使用 BINDER_SET_CONTEXT_MGR 命令透過 Binder 驅動將自己註冊成為 ServiceManager;
- Server 透過驅動向 ServiceManager 中註冊 Binder(Server 中的 Binder 實體),表明可以對外提供服務。驅動為這個 Binder 建立位於核心中的實體節點以及 ServiceManager 對實體的引用,將名字以及新建的引用打包傳給 ServiceManager,ServiceManger 將其填入查詢表。
- Client 透過名字,在 Binder 驅動的幫助下從 ServiceManager 中獲取到對 Binder 實體的引用,透過這個引用就能實現和 Server 程式的通訊
4.3 Binder 通訊中的代理模式
當 A 程式想要獲取 B 程式中的 object 時,驅動並不會真的把 object 返回給 A,而是返回了一個跟 object 看起來一模一樣的代理物件 objectProxy,這個 objectProxy 具有和 object 一摸一樣的方法,但是這些方法並沒有 B 程式中 object 物件那些方法的能力,這些方法只需要把把請求引數交給驅動即可。對於 A 程式來說和直接呼叫 object 中的方法是一樣的。當 Binder 驅動接收到 A 程式的訊息後,發現這是個 objectProxy 就去查詢自己維護的表單,一查發現這是 B 程式 object 的代理物件。於是就會去通知 B 程式呼叫 object 的方法,並要求 B 程式把返回結果發給自己。當驅動拿到 B 程式的返回結果後就會轉發給 A 程式,一次通訊就完成了
五、AIDL編碼實現跨程式呼叫
5.1 各 Java 類職責描述
- IBinder : IBinder 是一個介面,代表了一種跨程式通訊的能力。只要實現了這個藉口,這個物件就能跨程式傳輸。
- IInterface : IInterface 代表的就是 Server 程式物件具備什麼樣的能力(能提供哪些方法,其實對應的就是 AIDL 檔案中定義的介面)
- Binder : Java 層的 Binder 類,代表的其實就是 Binder 本地物件。BinderProxy 類是 Binder 類的一個內部類,它代表遠端程式的 Binder 物件的本地代理;這兩個類都繼承自 IBinder, 因而都具有跨程式傳輸的能力;實際上,在跨越程式的時候,Binder 驅動會自動完成這兩個物件的轉換。
- Stub : AIDL 的時候,編譯工具會給我們生成一個名為 Stub 的靜態內部類;這個類繼承了 Binder, 說明它是一個 Binder 本地物件,它實現了 IInterface 介面,表明它具有 Server 承諾給 Client 的能力;Stub 是一個抽象類,具體的 IInterface 的相關實現需要開發者自己實現。
5.2 AIDL實現過程
5.2.1 建立一個aidl檔案在裡面定義一個介面,介面繼承自IInterface,裡面定義服務端提供給客戶端的方法
5.2.2 透過Android studio編譯aidl檔案,編譯後生成對應的java檔案
這個java類首先宣告瞭AIDL檔案中定義的方法,同時宣告瞭兩個整形id用來標識這兩個方法。接著宣告瞭一個內部類Stub,這個Stub是一個binder類,
還定義了一個stub的內部代理類proxy。
總結:
- 檔案裡面包括了一個跨程式呼叫物件 Stub。Stub 繼承 Binder, 說明它是一個 Binder 本地物件;實現 IInterface 介面,表明具有 Server 承諾給 Client 的能力;Stub 是一個抽象類,具體的 IInterface 的相關實現需要呼叫方自己實現
- 在Stub 類中具有兩個方法 asInterface 和 onTransact。asInterface,當 Client 端在建立和服務端的連線,呼叫 bindService 時需要建立一個 ServiceConnection 物件作為入參。在 ServiceConnection 的回撥方法 onServiceConnected 中 會透過這個 asInterface(IBinder binder) 拿到 aidl 物件,這個 IBinder 型別的入參 binder 是驅動傳給我們的,方法中會去呼叫 binder.queryLocalInterface() 去查詢 Binder 本地物件,如果找到了就說明 Client 和 Server 在同一程式,那麼這個 binder 本身就是 Binder 本地物件,可以直接使用。否則說明是 binder 是個遠端物件,也就是 BinderProxy。因此需要我們建立一個代理物件 Proxy,透過這個代理物件來是實現遠端訪問。在java檔案中也定義了代理物件 Proxy。正如前文所述 Proxy 是在 Stub 的 asInterface 中建立,能走到建立 Proxy 這一步就說明 Proxy 建構函式的入參是 BinderProxy,即這裡的 remote 是個 BinderProxy 物件。最終透過一系列的函式呼叫,Client 程式透過系統呼叫陷入核心態,Client 程式中執行 addBook() 的執行緒掛起等待返回;驅動完成一系列的操作之後喚醒 Server 程式,呼叫 Server 程式本地物件的 onTransact()。最終又走到了 Stub 中的 onTransact() 中,onTransact() 根據函式編號呼叫相關函式(在 Stub 類中為介面中的每個函式中定義了一個編號,只不過上面的原始碼中我們簡化掉了;在跨程式呼叫的時候,不會傳遞函式而是傳遞編號來指明要呼叫哪個函式);我們這個例子裡面,呼叫了 Binder 本地物件的 addBook() 並將結果返回給驅動,驅動喚醒 Client 程式裡剛剛掛起的執行緒並將結果返回。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69981976/viewspace-2719444/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Binder通訊機制與IPC通訊.md
- Android 系統原始碼-2:Binder 通訊機制Android原始碼
- Binder機制
- Binder機制之AIDLAI
- Android高階進階之路【五】深入剖析Android系統Binder通訊機制Android
- Android Binder機制文章轉載Android
- Android進階(六)Binder機制Android
- 藉助 AIDL 理解 Android Binder 機制——Binder 來龍去脈AIAndroid
- 通訊機制 synchronous communication
- Elasticsearch NettyTransport通訊機制ElasticsearchNetty
- Linux程式通訊機制Linux
- 圖解Android中的binder機制圖解Android
- HTTPS的安全通訊機制HTTP
- 理解 Android Binder 機制(二):C++層AndroidC++
- Android IPC機制(三):淺談Binder的使用Android
- ReactNative原始碼篇:通訊機制React原始碼
- Android C++層使用Binder通訊的方法AndroidC++
- 3分鐘帶你看懂android的Binder機制Android
- Kubernetes的容器網路通訊機制
- Linux 程式間通訊的六種機制Linux
- 執行緒間通訊_等待/通知機制執行緒
- React Native原理之跨端通訊機制React Native跨端
- 一張圖進階 RocketMQ - 通訊機制MQ
- Android外掛化原理解析——Hook機制之Binder HookAndroidHook
- WebRTC 及點對點網路通訊機制Web
- ros的幾種通訊機制及程式碼ROS
- Java 執行緒間通訊 —— 等待 / 通知機制Java執行緒
- 螞蟻金服通訊框架 SOFABolt 解析 | 超時控制機制及心跳機制框架
- 藉助 AIDL 理解 Android Binder 機制——AIDL 的使用和原理分析AIAndroid
- PHP-FPM 與 Nginx 的通訊機制總結PHPNginx
- 深入探討程式間通訊的重要性:理解不同的通訊機制(下)
- 深入探討程式間通訊的重要性:理解不同的通訊機制(上)
- Android native程式間通訊例項-binder結合共享記憶體Android記憶體
- Linux環境程式設計程式間通訊機制理解Linux程式設計
- android 非同步通訊機制Handler的分析與運用Android非同步
- [原始碼解析] TensorFlow 分散式環境(8) --- 通訊機制原始碼分散式
- 螞蟻金服通訊框架SOFABolt解析|編解碼機制框架
- 深入探討微服務架構中的同步通訊機制微服務架構