Android進階學習——Binder是如何工作的?

隨着Android相關技術的不斷髮展,動態化,可插拔,插件化等越來越多的高端名詞不斷地湧現在我們的視野當中,那麼如何來學習這些技術,也就成爲了Android開發工程師需要重點關注的內容。

當然,萬物不離其原理,剛剛提到的這些技術,其實都基於Android系統的IPC機制,也就是我們今天的主角——Binder。

提到Binder,相信很多人都熟悉,但可能真正用到的人很少。因爲,除了一些特殊需求,如保活,後臺運行等之外,幾乎很少用到多進程,更不用提進程間通信了。但是技術總是在不斷更新的,新技術或多或少地都用到了Binder的知識,所以學習Binder的原理還是很重要的。

  • 什麼是Binder?

Binde的本意是“膠水”,“黏合劑”的意思,那麼對應到Android操作系統上,顧名思義,就是起到粘合不同進程的作用。在我們所熟知的Android四大組件,插件化,AIDL中,都有他的身影。

  • 爲什麼是Binder?

熟悉Android的人都知道,Android是基於Linux系統而來的,那麼我們自然能夠想到,對於Linux的IPC有管道,消息隊列,共享內存,Socket等,那麼爲什麼Android還要單獨開發一個Binder呢?

首先就是性能:

 

從上面的圖我們可以看到,使用了內存映射技術的Binder,他的數據拷貝次數是最小的,從而提升了通信效率,這對於一個移動操作系統來說,可是相當重要的,畢竟,哪個用戶都不能忍受APP點下去長時間沒反應的狀況出現。

其次就是穩定性:

Binder基於C/S架構,Client的任何請求都由ServiceManager完成,並由Binder驅動實現數據傳遞,得益於單次數據拷貝和內存映射技術,系統可靠性也得到了大幅度的保障。

最後就是安全性:

傳統的IPC方式,沒有任何安全措施,完全依靠上層協議來保障,並且UID是由用戶來填入的,很容易被惡意應用利用進而無法鑑別對方身份。而Binder則規避了這一點,UID由IPC在內核機制中進行添加。同時,Binder同時支持實名Binder和匿名Binder,安全性極高。

最後用一張表來展示下Binder的優勢:

 

  • Linux下的進程間通信原理

這裏有幾個比較重要的概念需要大家先來了解一下:

     1.進程隔離:

進程隔離的意思就是對於操作系統來說,進程間的內存是不共享的,不能進行直接通信,想要通信的話就只能藉助於IPC機制。

     2.進程控件劃分:用戶空間、內核空間

簡單地講,用戶空間就是供用戶進行應用運行的空間。而內核空間就是系統內核運行的空間,後者可以直接操作計算機的底層硬件。並且爲了保證安全性,他們兩個之間是相互隔離的。

     3.系統調用:用戶態、內核態

雖然用戶空間和內核空間相互隔離,但他倆之間還是不可避免地要產生一些數據交換,尤其是當用戶空間訪問內核空間時,例如我們所熟知的網絡訪問,數據存取等操作,都是通過用戶空間對內核空間的訪問和操作構成的。同時,只允許內核空間操作底層硬件這樣的操作也對系統提供了一種保護。

在Linux系統中,有一個兩級保護機制:0級供內核使用,3級供用戶程序使用。

當一個進程執行系統調用進入內核代碼時,此時進程處於內核態。每個進程都有自己的內核態。

當進程在執行用戶代碼的時候,進程處於用戶態,此時CPU在最低的3級用戶代碼中運行。

系統調用主要通過兩個命令來實現:

Copy_to_user();//從用戶空間拷貝到內核空間

Copy_from_user();//從內核空間拷貝到用戶空間

 

通過以上的瞭解,我們已經對Linux的IPC有了一個感性的認識,那麼在實際的操作過程中,他究竟是怎樣的一種運行機制呢?

先放一張圖:

 

可以看到,在數據發送進程和數據接收進程的IPC過程中,出現了兩次數據拷貝過程:

  1. 數據發送進程將數據通過copy_to_user從其用戶空間拷貝到了內核空間中的內核緩存區
  2. 數據接收進程將數據通過copy_from_user從內核緩存區拷貝到了其所在的用戶空間

 

這其中有兩個問題:

  1. 數據的傳遞過程經歷了兩次數據拷貝,消耗、佔用的資源較多,從用戶的角度看,就是系統的響應速度變慢了
  2. 接收數據的緩存區由數據接收進程提供,但其並不知道需要開闢多大的空間來進行數據接收,所以只能儘可能地開闢更大的空間來滿足接收需求。從用戶的角度來看,就是進程所佔用的內存會更大,造成比較嚴重的系統卡頓現象,相信這是每個開發者都不願意看到的狀況。

 

  • Binder的跨進程通信原理

首先要說的是,Binder通信是基於mmap來實現的。

傳統意義上的mmap是用來在物理存儲介質和用戶內存空間中建立映射關係的,用以減少數據的拷貝次數,直接從內存進行讀寫,提升文件吞吐效率。

在Binder中並不存在物理存儲介質,但我們仍可以利用他的映射特性來在內核空間和數據接收緩存空間中建立映射關係。

一次完整的Binder IPC通信過程通常是這樣:

  1. Binder驅動在內核空間創建一個數據接收緩衝區
  2. 接着在內核開闢一塊內核緩存區,並建立內核緩存區和內核中數據接收緩存區,以及內核中數據接收緩存區和接收用戶進程之間的映射關係。
  3. 發送方進程通過copy_from_user將數據copy到內核中的內核緩存區,由於映射的關係,數據在接收方的緩存區中也是可見的,減少了傳統意義上進程通信的數據接收拷貝過程,提升了數據傳輸效率。

流程圖如下:

 

五、ServiceManager與Client、Server的糾纏

通過上面的介紹,我們已經對Binder的數據通信過程有了一個感性的認識。

在實際的Android操作系統中,除了Client、Server、Binder驅動外,還有一個在用戶空間中的關鍵組件——ServiceManager,他的作用就相當於我們在HTTP訪問過程中的DNS服務器,用來將字符形式的Binder名稱轉化成Client中對Binder的引用,這樣,Client通過名稱就可以獲得對Server中Binder實體的引用。

具體的運行原理如下:

   1.Server的註冊過程:

Server創建了Binder實體,併爲這個實體取一個字符名字,在創建完之後,還需要申請向ServiceManager進行註冊,註冊完之後的Binder相當於同時在Server和ServiceManager中具有了引用對象.

值得注意的是,ServiceManager也是一個單獨的進程,但他與傳統意義上的進程略有區別,他的引用號是默認的,爲0,這樣,在它對外提供服務的時候,Client就可以通過0號引用來獲得對目標Server的引用了。

   2.Client的實名Binder獲取過程:

在Server向ServiceManager註冊完成之後,Client就可以通過它來獲取實體Binder的引用了,通過向0號進程,也就是我們的ServiceManager請求實體Binder的字符串名稱,ServiceManager就可以通過查詢的方式從之前註冊好的表中取出Binder實體,並將這個實體引用傳遞給Client進行使用。

 

  • Client如何調用Server中的對象

在Client獲得對Server中Binder對象的引用後,Client該如何調用Server中的對象及其方法呢?

當 A 進程想要獲取 B 進程中的 object 時,驅動並不會真的把 object 返回給 A,而是返回了一個跟 object 看起來一模一樣的代理對象 objectProxy,這個 objectProxy 具有和 object 一摸一樣的方法,但是這些方法並沒有 B 進程中 object 對象那些方法的能力,這些方法只需要把把請求參數交給驅動即可。對於 A 進程來說和直接調用 object 中的方法是一樣的。

當 Binder 驅動接收到 A 進程的消息後,發現這是個 objectProxy 就去查詢自己維護的表單,一查發現這是 B 進程 object 的代理對象。於是就會去通知 B 進程調用 object 的方法,並要求 B 進程把返回結果發給自己。當驅動拿到 B 進程的返回結果後就會轉發給 A 進程,一次通信就完成了。

 

寫在最後:Binder到底是什麼?

  • 通常意義下,Binder指的是一種通信機制;我們說AIDL使用Binder進行通信,指的就是Binder這種IPC機制
  • 對於Server進程來說,Binder指的是Binder本地對象
  • 對於Client來說,Binder指的是Binder代理對象,它只是Binder本地對象的一個遠程代理;對這個Binder代理對象的操作,會通過驅動最終轉發到Binder本地對象上去完成;對於一個擁有Binder對象的使用者而言,它無須關心這是一個Binder代理對象還是Binder本地對象;對於代理對象的操作和對本地對象的操作對它來說沒有區別。
  • 對於傳輸過程而言,Binder是可以進行跨進程傳遞的對象;Binder驅動會對具有跨進程傳遞能力的對象做特殊處理:自動完成代理對象和本地對象的轉換。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章