導航:首頁 > 操作系統 > android中binder機制

android中binder機制

發布時間:2023-03-22 13:16:14

① Carson帶你學android:全面剖析Binder跨進程通信原理

從而全方位地介紹 Binder ,希望你們會喜歡。

在本文的講解中,按照 大角度 -> 小角度 去分析 Binder ,即:

從而全方位地介紹 Binder ,希望你們會喜歡。

在講解 Binder 前,我們先了解一些 Linux 的基礎知識

具體請看文章: 操作系統:圖文詳解 內存映射

Binder 跨進程通信機制 模型 基於 Client - Server 模式

此處重點講解 Binder 驅動作用中的跨進程通信的原理:

原因:

所以,原理圖可表示為以下:

所以,在進行跨進程通信時,開發者只需自定義 Client & Server 進程 並 顯式使用上述3個步驟,最終藉助 Android 的基本架構功能就可完成進程間通信

注冊服務後, Binder 驅動持有 Server 進程創建的 Binder 實體

此時, Client 進程與 Server 進程已經建立了連接

Client 進程 根據獲取到的 Service 信息( Binder 代理對象),通過 Binder 驅動 建立與 該 Service 所在 Server 進程通信的鏈路,並開始使用服務

步驟1: Client 進程 將參數(整數a和b)發送到 Server 進程

步驟2: Server 進程根據 Client 進要求 調用 目標方法(即加法函數)

步驟3: Server 進程 將目標方法的結果(即加法後的結果)返回給 Client 進程

對比 Linux ( Android 基於 Linux )上的其他進程通信方式(管道、消息隊列、共享內存、
信號量、 Socket ), Binder 機制的優點有:

特別地,對於從模型結構組成的Binder驅動來說:

不定期分享關於 安卓開發 的干貨,追求 短、平、快 ,但 卻不缺深度

② Android源碼解析RPC系列(一)---Binder原理

看了幾天的Binder,決定有必要寫一篇博客,記錄一下學習成果,Binder是Android中比較綜合的一塊知識了,目前的理解只限於JAVA層。首先Binder是幹嘛用的?不用說,跨進程通信全靠它,操作系統的不同進程之間,數據不共享,對於每個進程來說,它都天真地以為自己獨享了整個系統,完全不知道其他進程的存在,進程之間需要通信需要某種系統機制才能完成,在Android整個系統架構中,採用了大量的C/S架構的思想,所以Binder的作用就顯得非常重要了,但是這種機制為什麼是Binder呢?在Linux中的RPC方式有管道,消息隊列,共享內存等,消息隊列和管道採用存儲-轉發方式,即數據先從發送方緩存區拷貝到內核開辟的緩存區中,然後再從內核緩存區拷貝到接收方緩存區,這樣就有兩次拷貝過程。共享內存不需要拷貝,但控制復雜,難以使用。Binder是個折中的方案,只需要拷貝一次就行了。其次Binder的安全性比較好,好在哪裡,在下還不是很清楚,基於安全性和傳輸的效率考慮,選擇了Binder。Binder的英文意思是粘結劑,Binder對象是一個可以跨進程引用的對象,它的實體位於一個進程中,這個進程一般是Server端,該對象提供了一套方法用以實現對服務的請求,而它的引用卻遍布於系統的各個進程(Client端)之中,這樣Client通過Binder的引用訪問Server,所以說,Binder就像膠水一樣,把系統各個進程粘結在一起了,廢話確實有點多。

為了從而保障了系統的安全和穩定,整個系統被劃分成內核空間和用戶空間
內核空間:獨立於普通的應用程序,可以訪問受保護的內存空間,有訪問底層硬體設備的所有許可權。
用戶空間:相對與內核空間,上層運用程序所運行的空間就是用戶空間,用戶空間訪問內核空間的唯一方式就是系統調用。一個4G的虛擬地址空間,其中3G是用戶空間,剩餘的1G是內核空間。如果一個用戶空間想與另外一個用戶空間進行通信,就需要內核模塊支持,這個運行在內核空間的,負責各個用戶進程通過Binder通信的內核模塊叫做Binder驅動,雖然叫做Binder驅動,但是和硬體並沒有什麼關系,只是實現方式和設備驅動程序是一樣的,提供了一些標准文件操作。

在寫AIDL的時候,一般情況下,我們有兩個進程,一個作為Server端提供某種服務,然後另外一個進程作為Client端,連接Server端之後,就 可以使用Server裡面定義的服務。這種思想是一種典型的C/S的思想。值得注意的是Android系統中的Binder自身也是C/S的架構,也有Server端與Client端。一個大的C/S架構中,也有一個小的C/S架構。

先籠統的說一下,在整個Binder框架中,由系列組件組成,分別是Client、Server、ServiceManager和Binder驅動程序,其中Client、Server和ServiceManager運行在用戶空間,Binder驅動程序運行內核空間。運行在用戶空間中的Client、Server和ServiceManager,是在三個不同進程中的,Server進程中中定義了服務提供給Client進程使用,並且Server中有一個Binder實體,但是Server中定義的服務並不能直接被Client使用,它需要向ServiceManager注冊,然後Client要用服務的時候,直接向ServiceManager要,ServiceManager返回一個Binder的替身(引用)給Client,這樣Client就可以調用Server中的服務了。

場景 :進程A要調用進程B裡面的一個draw方法處理圖片。

分析 :在這種場景下,進程A作為Client端,進程B做為Server端,但是A/B不在同一個進程中,怎麼來調用B進程的draw方法呢,首先進程B作為Server端創建了Binder實體,為其取一個字元形式,可讀易記的名字,並將這個Binder連同名字以數據包的形式通過Binder驅動發送給ServiceManager,也就是向ServiceManager注冊的過程,告訴ServiceManager,我是進程B,擁有圖像處理的功能,ServiceManager從數據包中取出名字和引用以一個注冊表的形式保留了Server進程的注冊信息。為什麼是以數據包的形式呢,因為這是兩個進程,直接傳遞對象是不行滴,只能是一些描述信息。現在Client端進程A聯系ServiceManager,說現在我需要進程B中圖像處理的功能,ServiceManager從注冊表中查到了這個Binder實體,但是呢,它並不是直接把這個Binder實體直接給Client,而是給了一個Binder實體的代理,或者說是引用,Client通過Binder的引用訪問Server。分析到現在,有個關鍵的問題需要說一下,ServiceManager是一個進程,Server是另一個進程,Server向ServiceManager注冊Binder必然會涉及進程間通信。當前實現的是進程間通信卻又要用到進程間通信,這就好象蛋可以孵出雞前提卻是要找只雞來孵蛋,確實是這樣的,ServiceManager中預先有了一個自己的Binder對象(實體),就是那隻雞,然後Server有個Binder對象的引用,就是那個蛋,Server需要通過這個Binder的引用來實現Binder的注冊。雞就一隻,蛋有很多,ServiceManager進程的Binder對象(實體)僅有一個,其他進程所擁有的全部都是它的代理。同樣一個Server端Binder實體也應該只有一個,對應所有Client端全部都是它的代理。

我們再次理解一下Binder是什麼?在Binder通信模型的四個角色裡面;他們的代表都是「Binder」,一個Binder對象就代表了所有,包括了Server,Client,ServiceManager,這樣,對於Binder通信的使用者而言,不用關心實現的細節。對Server來說,Binder指的是Binder實體,或者說是本地對象,對於Client來說,Binder指的是Binder代理對象,也就是Binder的引用。對於Binder驅動而言,在Binder對象進行跨進程傳遞的時候,Binder驅動會自動完成這兩種類型的轉換。

簡單的總結一下,通過上面一大段的分析,一個Server在使用的時候需要經歷三個階段

1、定義一個AIDL文件
Game.aidl

GameManager .aidl

2、定義遠端服務Service
在遠程服務中的onBind方法,實現AIDL介面的具體方法,並且返回Binder對象

3、本地創建連接對象

以上就是一個遠端服務的一般套路,如果是在兩個進程中,就可以進程通信了,現在我們分析一下,這個通信的流程。重點是GameManager這個編譯生成的類。

從類的關系來看,首先介面GameManager 繼承 IInterface ,IInterface是一個介面,在GameManager內部有一個內部類Stub,Stub繼承了Binder,(Binder實現了IBinder),並且實現了GameManager介面,在Stub中還有一個內部類Proxy,Proxy也實現了GameManager介面,一個整體的結構是這樣的

現在的問題是,Stub是什麼?Proxy又是什麼?在上面說了在Binder通信模型的四個角色裡面;他們的代表都是「Binder」,一個Binder對象就代表了所有,包括了Server,Clinet,ServiceManager,為了兩個進程的通信,系統給予的內核支持是Binder,在抽象一點的說,Binder是系統開辟的一塊內存空間,兩個進程往這塊空間裡面讀寫數據就行了,Stub從Binder中讀數據,Proxy向Binder中寫數據,達到進程間通信的目的。首先我們分析Stub。

Stub 類繼承了Binder ,說明了Stub有了跨進程傳輸的能力,實現了GameManager介面,說明它有了根據游戲ID查詢一個游戲的能力。我們在bind一個Service之後,在onServiceConnecttion的回調裡面,就是通過asInterface方法拿到一個遠程的service的。

asInterface調用queryLocalInterface。

mDescriptor,mOwner其實是Binder的成員變數,Stub繼承了Binder,在構造函數的時候,對著兩個變數賦的值。

如果客戶端和服務端是在一個進程中,那麼其實queryLocalInterface獲取的就是Stub對象,如果不在一個進程queryLocalInterface查詢的對象肯定為null,因為不同進程有不同虛擬機,肯定查不到mOwner對象的,所以這時候其實是返回的Proxy對象了。拿到Stub對象後,通常在onServiceConnected中,就把這個對象轉換成我們多定義AIDL介面。

比如我們這里會轉換成GameManager,有了GameManager對象,就可以調用後querryGameById方法了。如果是一個進程,那直接調用的是自己的querryGameById方法,如果不是一個進程,那調用了就是代理的querryGameById方法了。

看到其中關鍵的一行是

mRemote就是一個IBinder對象,相對於Stub,Proxy 是組合關系(HAS-A),內部有一個IBinder對象mRemote,Stub是繼承關系(IS-A),直接實現了IBinder介面。

transact是個native方法,最終還會回掉JAVA層的onTransact方法。

onTransact根據調用號(每個AIDL函數都有一個編號,在跨進程的時候,不會傳遞函數,而是傳遞編號指明調用哪個函數)調用相關函數;在這個例子裡面,調用了Binder本地對象的querryGameById方法;這個方法將結果返回給驅動,驅動喚醒掛起的Client進程裡面的線程並將結果返回。於是一次跨進程調用就完成了。

***Please accept mybest wishes for your happiness and success ! ***

③ Android通信方式篇(七)-Binder機制(Native層(下))

本篇文章針對向ServiceManager注冊服務 和 獲取服務兩個流程來做總結。在這兩個過程中,ServiceManager都扮演的是服務端,與客戶端之間的通信也是通過Binder IPC。

在此之前先了解下Binder的進程與線程的關系:

用戶空間 :ProcessState描述一個進程,IPCThreadState對應一個進程中的一個線程。
內核空間 :binder_proc描述一個進程,統一由binder_procs全局鏈表保存,binder_thread對應進程的一個線程。
ProcessState與binder_proc是一一對應的。

Binder線程池 :每個Server進程在啟動時會創建一個binder線程池,並向其中注冊一個Binder線程;之後Server進程也可以向binder線程池注冊新的線程,或者Binder驅動在探測到沒有空閑binder線程時會主動向Server進程注冊新的的binder線程。對於一個Server進程有一個最大Binder線程數限制15,(#define DEFAULT_MAX_BINDER_THREADS 15)。對於所有Client端進程的binder請求都是交由Server端進程的binder線程來處理的。我的理解是:binder線程是進程進行binder ipc時的一條數據處理路徑。

MediaPlayerService向ServiceManager注冊過程如下:

相關類:

整個過程總結如下:
1 獲取BpServiceManager 與 BpBinder
由defaultServiceManager()返回的是BpServiceManager,同時會創建ProcessState對象和BpBinder對象。然後通過BpBinder執行transact,把真正工作交給IPCThreadState來處理。

2 BpBinder transact
Binder代理類調用transact()方法,真正工作還是交給IPCThreadState來進行transact工作。

3 通過IPCThreadState 包裝並轉換數據並進行transact事務處理
每個線程都有一個IPCThreadState,每個IPCThreadState中都有一對Parcel變數:mIn、mOut。相當於兩根數據管道:

最後執行talkWithDriver。

writeTransactionData:將BC Protocol + binder_transaction_data結構體 寫入mOut, 然後執行waitForResponse:

由talkWithDriver將數據進一步封裝到binder_write_read結構體,通過ioctl(BINDER_WRITE_READ)與驅動通信。同時等待驅動返回的接收BR命令,從mIn取出返回的數據。

mIn包裝的數據結構(注冊服務handle = 0 ,code 為ADD_SERVICE_TRANSACTION):

4 Binder Driver
把binder_write_read結構體write_buffer里數據取出來,分別得到BC命令和封裝好數據的事務binder_transaction_data, 然後根據handler,在當前binder_proc中,找到相應的binder_ref,由binder_ref再找到目標binder_node實體,由目標binder_node再找到目標進程binder_proc。然後就是插入數據:當binder驅動可以找到合適的線程,就會把binder_transaction節點插入到servciemanager的線程的todo隊列中,如果找不到合適的線程,就把節點之間插入servciemanager的binder_proc的todo隊列。

5 ServiceManager
經過Binder Driver的處理,數據已經到了ServiceManager進程,在BR_TRANSACTION的引導下,在binder_loop()中執行binder_parser()取出數據,執行do_add_service()操作,最終向 svcinfo 列表中添加已經注冊的服務(沒有數據的返回)。最後發送 BR_REPLY 命令喚醒等待的線程,通知注冊成功。結束MediaPlayerService進程 waitForResponse()的狀態,整個注冊過程結束。

獲取服務的過程與注冊類似,首先 ServiceManager 向 Binder 驅動發送 BC_TRANSACTION 命令攜帶 CHECK_SERVICE_TRANSACTION 命令,同時獲取服務的線程進入等待狀態 waitForResponse()。Binder 驅動收到請求命令向 ServiceManager 的發送 BC_TRANSACTION 查詢已注冊的服務,會區分請求服務所屬進程情況。

查詢到直接響應 BR_REPLY 喚醒等待的線程。若查詢不到將與 binder_procs 鏈表中的服務進行一次通訊再響應。

以startService為例來簡單總結下執行流程:

3.1 從方法執行流程來看:

Client :

1 AMP.startService 標記方法以及通過Parcel包裝數據;

2 BinderProxy.transact 實際調用native的 android_os_BinderProxy_transact 傳遞數據;

3 獲取BpServiceManager 與 BpBinder 同時會創建ProcessState。然後通過BpBinder執行transact,把真正工作交給IPCThreadState來處理;

4 IPC.transact 主要執行writeTransactionData,將上層傳來的數據重新包裝成binder_transaction_data,並將BC Protocol + binder_transaction_data結構體 寫入mOut;

5 IPC waitForResponse talkWithDriver + 等待返回數據;

6 talkWithDriver 將數據進一步封裝成binder_write_read,通過ioctl(BINDER_WRITE_READ)與驅動通信;

Kernel :

7 binder ioctl 接收BINDER_WRITE_READ ioctl命令;

8 binder_ioctl_write_read 把用戶空間數據ubuf拷貝到內核空間bwr;

9 binder_thread_write 當bwr寫緩存有數據,則執行binder_thread_write;當寫失敗則將bwr數據寫回用戶空間並退出;

10 binder_transaction 找到目標進程binder_proc並插入數據到目標進程的線程todo隊列,最終執行到它
時,將發起端數據拷貝到接收端進程的buffer結構體;

11 binder_thread_read 根據binder_transaction結構體和binder_buffer結構體數據生成新的binder_transaction_data結構體,寫入bwr的read_buffer,當bwr讀緩存有數據,則執行binder_thread_read;當讀失敗則再將bwr數據寫回用戶空間並退出;最後,把內核數據bwr拷貝到用戶空間ubuf。

12 binder_thread_write + binder_ioctl BR命令和數據傳遞

Server:

13 IPC.executeCommand 解析kernel傳過來的binder_transaction_data數據,找到目標BBinder並調用其transact()方法;

14 IPC.joinThreadPool 採用循環不斷地執行getAndExecuteCommand()方法, 處理事務。當bwr的讀寫buffer都沒有數據時,則阻塞在binder_thread_read的wait_event過程. 另外,正常情況下binder線程一旦創建則不會退出.

15 BBinder.transact 到Binder.exeTransact 調用 AMN.onTransact

16 AMN.onTransact 把數據傳遞到AMS.starService去執行

17 AMS.starService Server處理了Client的請求了

然後原路replay回去,talkWithDriver 到Kernel ,然後找到Client進程,把數據拷貝到read_buffer里,最終喚醒IPC,把反饋傳遞回AMP.startService。完成啟動服務。

3.2 從通信協議流程來看:

非oneWay:

oneway:

oneway與非oneway區別: 都是需要等待Binder Driver的回應消息BR_TRANSACTION_COMPLETE. 主要區別在於oneway的通信收到BR_TRANSACTION_COMPLETE則返回,而不會再等待BR_REPLY消息的到來. 另外,oneway的binder IPC則接收端無法獲取對方的pid.

3.3 從數據流來看

從用戶空間開始:

進入驅動後:

回到用戶空間:

參考:
http://gityuan.com/2016/09/04/binder-start-service/
http://gityuan.com/2015/11/28/binder-summary/
http://gityuan.com/2015/11/14/binder-add-service/
http://gityuan.com/2015/11/15/binder-get-service/

閱讀全文

與android中binder機制相關的資料

熱點內容
蘋果筆記本t2加密晶元怎麼打開 瀏覽:796
安卓如何把手機投屏至電視 瀏覽:737
方舟編譯器現在可提速哪些軟體 瀏覽:58
微信加密為什麼是黑屏 瀏覽:473
android去電狀態 瀏覽:602
蘋果13如何加密視頻 瀏覽:813
linuxweblogic緩存 瀏覽:67
雲伺服器不同地域 瀏覽:946
python鬧鍾怎麼打 瀏覽:686
虛擬主機伺服器有什麼區別 瀏覽:833
演算法與程序的奧秘章節檢測 瀏覽:377
找pdf 瀏覽:529
與伺服器連接斷開如何處理 瀏覽:833
伺服器維修預計十分鍾什麼意思 瀏覽:170
黑馬程序員主打教學是什麼 瀏覽:41
python九乘法表怎麼編寫 瀏覽:974
思維方式pdf 瀏覽:656
tcc社區app怎麼注冊 瀏覽:941
央視網下載加密 瀏覽:454
命令行訪問伺服器 瀏覽:36