摘要:本節主要來講解Android10.0 Binder的通信原理總結
閱讀本文大約需要花費17分鐘。
文章首發微信公衆號:IngresGe
專注於Android系統級源碼分析,Android的平臺設計,歡迎關注我,謝謝!
[Android取經之路] 的源碼都基於Android-Q(10.0) 進行分析
[Android取經之路] 系列文章:
《系統啓動篇》
- Android系統架構
- Android是怎麼啓動的
- Android 10.0系統啓動之init進程
- Android10.0系統啓動之Zygote進程
- Android 10.0 系統啓動之SystemServer進程
- Android 10.0 系統服務之ActivityMnagerService
- Android10.0系統啓動之Launcher(桌面)啓動流程
- Android10.0應用進程創建過程以及Zygote的fork流程
- Android 10.0 PackageManagerService(一)工作原理及啓動流程
- Android 10.0 PackageManagerService(二)權限掃描
- Android 10.0 PackageManagerService(三)APK掃描
- Android 10.0 PackageManagerService(四)APK安裝流程
《日誌系統篇》
- Android10.0 日誌系統分析(一)-logd、logcat 指令說明、分類和屬性
- Android10.0 日誌系統分析(二)-logd、logcat架構分析及日誌系統初始化
- Android10.0 日誌系統分析(三)-logd、logcat讀寫日誌源碼分析
- Android10.0 日誌系統分析(四)-selinux、kernel日誌在logd中的實現
《Binder通信原理》:
- Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
- Android10.0 Binder通信原理(二)-Binder入門篇
- Android10.0 Binder通信原理(三)-ServiceManager篇
- Android10.0 Binder通信原理(四)-Native-C\C++實例分析
- Android10.0 Binder通信原理(五)-Binder驅動分析
- Android10.0 Binder通信原理(六)-Binder數據如何完成定向打擊
- Android10.0 Binder通信原理(七)-Framework binder示例
- Android10.0 Binder通信原理(八)-Framework層分析
- Android10.0 Binder通信原理(九)-AIDL Binder示例
- Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub設計模式
- Android10.0 Binder通信原理(十一)-Binder總結
1.概述
Binder的通信原理基本上都已經說完,這一節我們做一個簡單的概要總結。
2.Binder通信模型
下圖中涉及到Binder模型的4類角色:Binder驅動,ServiceManager,Server和Client。Binder機制的目的是實現IPC(Inter-Process Communication),即Client和Server之間的通信。
其中Server,Client,ServiceManager運行於用戶空間,Binder驅動運行於內核空間。這四個角色的關係和互聯網類似:Server是服務器,Client是客戶終端,ServiceManager是域名服務器(DNS),驅動是路由器。
3.Binder的架構
4.Binder的通信原理
Binder 通信採用 C/S 架構,從組件視角來說,包含 Client、 Server、 ServiceManager 以及 Binder 驅動,其中 ServiceManager 用於管理系統中的各種服務。
Binder 在 framework 層進行了封裝,通過 JNI 技術調用 Native(C/C++)層的 Binder 架構。
Binder 在 Native 層以 ioctl 的方式與 Binder 驅動通訊。
Binder通信流程如下:
- 首先服務端需要向ServiceManager進行服務註冊,ServiceManager有一個全局的service列表svcinfo,用來緩存所有服務的handler和name。
- 客戶端與服務端通信,需要拿到服務端的對象,由於進程隔離,客戶端拿到的其實是服務端的代理,也可以理解爲引用。客戶端通過ServiceManager從svcinfo中查找服務,ServiceManager返回服務的代理。
- 拿到服務對象後,我們需要向服務發送請求,實現我們需要的功能。通過 BinderProxy 將我們的請求參數發送給 內核,通過共享內存的方式使用內核方法 copy_from_user() 將我們的參數先拷貝到內核空間,這時我們的客戶端進入等待狀態。然後 Binder 驅動向服務端的 todo 隊列裏面插入一條事務,執行完之後把執行結果通過 copy_to_user() 將內核的結果拷貝到用戶空間(這裏只是執行了拷貝命令,並沒有拷貝數據,binder只進行一次拷貝),喚醒等待的客戶端並把結果響應回來,這樣就完成了一次通訊。
在這裏其實會存在一個問題,Client和Server之間通信是稱爲進程間通信,使用了Binder機制,那麼Server和ServiceManager之間通信也叫進程間通信,Client和Server之間還會用到ServiceManager,也就是說Binder進程間通信通過Binder進程間通信來完成,這就好比是 孵出雞前提卻是要找只雞來孵蛋,這是怎麼實現的呢?
Binder的實現比較巧妙:預先創造一隻雞來孵蛋:ServiceManager和其它進程同樣採用Binder通信,ServiceManager是Server端,有自己的Binder對象(實體),其它進程都是Client,需要通過這個Binder的引用來實現Binder的註冊,查詢和獲取。
ServiceManager提供的Binder比較特殊,它沒有名字也不需要註冊,當一個進程使用BINDER_SET_CONTEXT_MGR_EXT命令將自己註冊成ServiceManager時Binder驅動會自動爲它創建Binder實體(這就是那隻預先造好的雞)。
其次這個Binder的引用在所有Client中都固定爲0(handle=0)而無須通過其它手段獲得。也就是說,一個Server若要向ServiceManager註冊自己Binder就必須通過0這個引用號和ServiceManager的Binder通信。
類比網絡通信,0號引用就好比域名服務器的地址,你必須預先手工或動態配置好。要注意這裏說的Client是相對ServiceManager而言的,一個應用程序可能是個提供服務的Server,但對ServiceManager來說它仍然是個Client。
圖片來源csdn-jeanboydev
5 Binder傳輸過程
Binder-IPC機制,就是指在進程間傳輸數據(binder_transaction_data),一次數據的傳輸,稱爲事務(binder_transaction)。
對於多個不同進程向同一個進程發送事務時,這個同一個進程或線程的事務需要串行執行,在Binder驅動中爲binder_proc和binder_thread都有todo隊列。
也就是說對於進程間的通信,就是發送端把binder_transaction節點,插入到目標進程或其子線程的todo隊列中,等目標進程或線程不斷循環地從todo隊列中取出數據並進行相應的操作。
在Binder驅動層,每個接收端進程都有一個todo隊列,用於保存發送端進程發送過來的binder請求,這類請求可以由接收端進程的任意一個空閒的binder線程處理。
接收端進程存在一個或多個binder線程,在每個binder線程裏都有一個todo隊列,也是用於保存發送端進程發送過來的binder請求,這類請求只能由當前binder線程來處理。
binder線程在空閒時進入可中斷的休眠狀態,當自己的todo隊列或所屬進程的todo隊列有新的請求到來時便會喚醒,如果是由所需進程喚醒的,那麼進程會讓其中一個線程處理響應的請求,其他線程再次進入休眠狀態。
6 Binder協議的演變
下面展示了Binder協議碼的演變過程,在Android9.0之前,當client向Binder驅動發送BC_TRANSACTION,Binder驅動喚醒Server進程時,會向client進程發送BR_TRANSACTION_COMPLETE,現在這一步被移到了 喚醒Client之後再做,減少了數據延遲。
Android9.0之前的協議碼流程:
Android9.0及之後的協議碼流程:
上面第5步的 BR_TRANSACTION_COMPLETE 被延遲到 第 10步 ,Android做了deferred_thread_work,延遲 TRANSACTION_COMPLETE,因此不會立即返回到用戶空間;這允許目標進程立即開始處理此事務,從而減少延遲。然後,當目標回覆(或出現錯誤)時,我們將返回TRANSACTION_COMPLETE。
7.AIDL的Proxy-Stub設計模式
AIDL的設計採用了Proxy-Stub(代理-存根) 的設計模式,Client拿到的是Proxy的Binder代理對象,Server拿到的是Stub的Binder服務實體,兩者之間的數據傳入通過Parcel進行扁平化傳輸。
Proxy將特殊性接口轉換成通用性接口,Stub將通用性接口轉換成特殊性接口,二者之間的數據轉換通過Parcel(打包)進行的,Proxy常作爲數據發送代理,通過Parcel將數據打包發送,Stub常作爲數據接收樁,解包並解析Parcel Data package。
Client和Server交互的簡單示意流程:
Binder通信的數據流轉如下圖所示:
AIDL的具體流程如下:
- Client和Server都使用同一個AIDL文件,包名相同,編譯後,兩邊都會生成IMyService.java,其中有Stub實體和Proxy代理兩個對象
- Server端通過AndroidManifest.xml 註冊Service
- Client通過bindService()獲得服務的代理Stub.Proxy()
- Client 調用AIDL的方法add(),其實調用的是IMyService.java中的Stub.Proxy.add(),最終通過BinderProxy.java的transact()向服務端發送
- 通過Binder驅動的流程,進入到服務端的onTransact(),根據Client發送的TRANSACTION code,解析進入相應的流程處理,進入add()
- MyService在被綁定時,有了實體IMyService.Stub,最終進入MyService.java的add()處理,完成接口調用,調用完成後把數據寫入Parcel,通過reply發送給Client
我的微信公衆號:IngresGe