Binder問題分享

1Binder架構簡介

     Binder是一種IPC通訊機制,在android系統中處於核心地位,幾乎全部的跨進程通訊,或者進程內部系統組件通訊都是經過Binder進行交互。 Binder如同其字面意思,將系統各部件粘結起來,造成一個有機的總體。若是不能很好地理解Binder,就確定不能很好地理解android系統內部運行機制。Binder基於C/S架構而設計,Binder服務端注重優質服務的實現,Binder客戶端只需向服務端按事先約定好的協議訪問Binder服務端便可,而具體的通訊通道創建、請求發起、請求接入、呼應返回由相應的Binder通訊模塊完成。java

        圖1描述了Binder架構簡圖,圖中將Binder架構分爲FramworkLibraryKernel三大部分,該三大部分也是對應於Android系統的FrameworkLibraryKernelandroid

1 Binder架構簡圖程序員

  • Binder Framwork架構

包含Camera服務、Activity服務、Window服務、Power管理服務等,是App開發人員最熟悉的一層。app

    Framework左邊包括ActivityManagerWindowManager等系統服務客戶端,經過Context.getSystemService方法取得,每一個Manager內部定義了一個BinderProxy,用於向遠端Binder服務發起IPC調用,並接收處理結果。框架

    Framework右邊所括ActivityManagerServiceWindowManagerServicePowerManagerServiceBinder服務端,用於接收BinderProxy發來的IPC調用請求,並返回處理結果,這些service運行於SystemServer進程。ide

    Binder Framework使用Java語言編寫成,主要是爲Application層應用提供API及系統服務。Binder Framework經編譯打包後,位於framework.jar中。函數


  • Binder Librarygoogle

包含BpBinderBBinder/JavaBBinderProcessStateIPCThreadState等組件。編碼

       其中BpBinder對應Binder FrameworkBinderProxy,由JNI層的android_util_BinderProxy進行聯結,做用是將Binder啓用Binder驅動傳輸調用方法代碼、調用參數以及等待IPC調用返回結果。

BBinder (JavaBBinder)對應Binder FrameworkBinder,當BBinder接收到Binder客戶端調用請求後,經過JNI

       調用將調用請求轉交給Binder FrameworkBinder對象,而後等待Binder對象返回處理結果,再將結果轉交給底層傳輸回Binder客戶端。

    ProcessStateIPCThreadStateBinder框架底層通訊重要的兩個類,可歸屬於Binder HAL層的內容。

每個App或進程,對應惟一一個ProcessState實例,該實例持有當前進程與Binder驅動的通訊狀態,定義了非系統AppBinder空間大小、Binder線程數等信息,負責打開、維護、關閉/dev/binder設備。ProcessStateandroid_util_Binder.cpp中的android_util_BinderInternal的方法中初始化。

2描述了 ProcessState初始化的代碼,其主要工做是打開Binder驅動設備,而後將Binder設備mmap到當前進程的地址空間,地址空間範圍通常爲1MB-8KB,起始地址由Linux系統調用mmap自行根據當前進程狀況肯定。

2 ProcessState初始化代碼

        圖3描述了ProcessState打開Binder驅動設備的過程。

3 ProcessState打開Binder設備代碼

IPCThreadState記錄了IPC線程的狀態,用於與系統內核中的Binder驅動進行具體的交互通訊,通訊方式使用Linux系統調用ioctl。每接收到一個新的Binder客戶端請求,Binder服務端會從新創建一個線程和IPCThreadState記錄對該Binder客戶端的通訊,最大IPC線程數爲15,在ProcessState中定義,見圖4

4 ProcessState常量定義


  • Binder Kernel

       該部分模塊運行於Linux系統內核,包含Binder驅動,Binder LibraryBinder Kernel使用ioctl進行讀寫操做。

相比較其餘的IPC通訊,好比消息機制、共享內存、管道、信號量等,Binder僅需一次內存拷貝,便可讓目標進程讀取到更新數據,同共享內存同樣至關高效,其餘的IPC通訊機制大多須要2次內存拷貝。

       圖5描述了Binder內存拷貝的原理示意圖,進程ABinder客戶端,在IPC調用前,需將其用戶空間的數據拷貝到Binder驅動的內核空間,因爲進程B在打開Binder設備(/dev/binder)時,已將Binder驅動的內核空間映射(mmap)到本身的進程空間,因此進程B能夠

       直接看到Binder驅動內核空間的內容改動。由於Windows/Linux系統,內核空間範圍大都爲1GB,因此內核空間通常比較有限,因此Binder驅動的內存地址空間也相對較小。

 圖5 Binder內存拷貝示意圖


2Binder崩潰問題

      在XX項目中,Binder異常致使的崩潰時不時會出現一次,圖6爲一例Binder崩潰日誌。

6 Binder崩潰日誌

Binder崩潰,究其緣由是Googleandroid 6.0後,調整了Framework層在捕獲到Binder通訊過程當中產生的異常的錯誤處理機制,android 6.0以前,Framework捕獲到binder通訊的異常,並不將異常再拋給應用端,而6.0以後,Framework捕獲到binder通訊異常,轉而從新包裝一下該異常爲新的異常,再將新異常從新拋出,其目的是讓應用端能更好地更合理地對Binder通訊異常的處理。

       圖7ActivityManager.getRunningAppProcesses方法的7.1版本實現,很明顯方法在捕獲到RemoteExcetion後轉拋出DeadSystemExcetion

       圖8ActivityManager.getRunningAppProcesses方法的6.0版本實現,方法捕獲到RemoteException後返回null

7 AcitivityManager方法的anroid 7.1版本

8 AcitivityManager方法的anroid 6.0版本

       圖9分析了圖6崩潰日誌更深一些的方法調用棧,DeadObjectException等異常拋出點在android_util_Binder.cpp中的signalExceptionForError函數。

9 Binder崩潰方法調用棧示例


       圖10描述了android_util_Binder.cpp中的signalExceptionForError函數定義。

10 Binder異常產生源頭代碼


       圖11描述了android_util_Binder.cpp中的android_os_BinderProxy_transact函數,即Binder.javaBinderProxy.transactNativeC/C++實現。

11 Binder異常產生源頭代碼


3Binder問題彙總

       圖12描述了Binder問題的彙總狀態。從反饋的log信息概括,白牌項目遇到的崩潰集中在DeadObject,爲圖12中黃色背景部分的內容,另一個Binder異常是TransactionTooLargeException,是咱們本身爲重現Binder崩潰而遇到的異常。

12 Binder問題彙總


      Binder崩潰問題之因此複雜,除了Binder自己設計及架構的複雜,還有使用模塊及使用方式的複雜性,對於排錯、尋找解決方案增長了很大的困難。圖13顯示了App代碼三種主要的調用Binder路徑,第一種是App直接Binder調用系統服務;第二種方式App先調用Android SDKSDKBinder調用系統服務,而系統服務可能Binder回調App接口,也有可能Binder調用其餘系統服務,或者調用SDK API;第三種是App調用本身定義的Binder服務,自定義服務再Binder調用系統服務,或者調用SDK API

          圖13展現了Binder調用的廣度和調用層次的深度,其調用深度甚至可達到無究大,意味着圈定Binder問題可能的發生地或統籌Binder問題發生地分佈,幾乎是件不可能的事。所以,緩解Binder問題比較好的思路是攔截思路或在dalvik修改相關類的字節碼。

13 Binder問題產生路徑

4、解決方案演變歷程

4.1方案一

採用IBinder.linkToDeath註冊IBinder.DeathRecipient回調。

       大體思路是Binder客戶端調用IBinderlinkToDeath方法註冊回調IBinder.DeathRecipient,以便於當Binder服務端掛掉時,可即時收到Binder服務端崩潰的消息,此後Binder客戶端可啓動錯誤處理機制,或等待Binder服務端恢復運行,而後再繼續訪問。但該方案有一個盲點,如圖14所示,當Binder服務端崩潰時,因爲是C/S架構,服務端崩潰消息到達客戶端須要必定的時間,若是客戶端在服務端崩潰消息到達前,仍繼續IPC調用服務端接口,則仍然有可能收到DeadObjectException,這是該方法的盲點。另外,使用此方案,也要求App在設計之初就要作相應的容錯處理機制,而這種機制是大多數老應用程序所不俱備的,究其緣由是Binder機制雖是系統重要的機制,但其被各類上層封裝所掩埋,大多數程序員不易直接接觸Binder機制或Binder機制引出的問題,因此在設計、編寫程序時不會考慮到Binder服務端掛掉時的錯誤處理,這種處理和通常的異常處理是不同的,涉及到跨進程訪問,以及UI即時響應,以避免ANR等方面。

        系統服務間大多也採用該方案監聽遠端服務的運行狀態,好比SurfaceFlinger監聽WindowManagerService

        該方案因引入成本過高,目前暫不引入項目,不過對App後續新模塊的開發有指導意義,可基於方案寫出一套完善的、基於IPC調用的適於android系統的較強魯棒性的功能模塊。

14 Binder C/S架構蔽端

       圖15爲方案一的代碼示意。

15方案一代碼示意


4.2方案二

         使用Hook攔截Framework層服務異常。

思路

         使用InvocationHandler動態代理系統服務類,在此基礎上try-catch住系統服務的RemoteException。圖16中類ServiceInterceptorinvoke方法爲本方案核心,主要思路是在IPC調用前使用Binder.flushPendingCommands釋放Binder內存,而後進入try-catch內動態代理調用系統服務方法,若是系統服務拋出異常,流程則轉至catch(RemoteException rex)處理,最後在invoke方法返回前再次調用Binder.flushPendingCommands清理Binder內存。


方案優勢

         能夠捕獲系統服務異常並處理。


方案缺點

   Invoke方法在捕獲到異常後,返回null值或0值給調用者,若是調用者沒有對返回結果進行校驗,有可能會致使NullPointerException或業務邏輯不正確。

16方案二代碼示意



4.3展望

  • 方案三

同方法二,只是攔截異常地點不一樣,是在BinderProxytransact方法進行攔截。

BinderProxytransact方法是Binder客戶端的消息集散地,如同HandlerhandleMessage,在此截獲消息,首先是能夠掌握進出本應用的消息流動態,其次是方便攔截和藹後處理。

17BinderProxy的類定義,對於Binder崩潰問題,transact方法是關鍵,DeadObjecException是由transact內部部調用JNI方法transactNative產生,在transact捕獲DeadObjectException,實爲java層第一時間捕獲,防止該異常繼續向java層發散。

17 BinderProxy類定義

        圖17AIDL封裝Binder客戶端的代碼示意,mRemoteBinderProxy實例,從圖18中可看出,AIDL封裝並不關心mRemote.transact的返回狀態值,具體值包裝在_reply中。

18 AIDL客戶端方法示意


19、圖20爲如何攔截AmsBinderProxy.transact方法的代碼示意。注意,每一個服務的編碼結構不同,須要適當調整代碼以攔截BinderProxy異常。

19攔截代碼示意

20 攔截代碼示例

  • 方案四:

該方法是逆向工程的思路,須要瞭解Dalvik內部運行機制,採用靜態/動態修改dex字節碼方式,修改BinderProxytransact方法。


5、總結

Binder崩潰問題,反映的是Android6.0之後的版本一改以前各版本處理方式,GoogleFrameworkBinder調用產生的異常直接拋給App層,讓Android6.0之前的寫好的App猝不及防,並且google也沒有明確提出這方面的最佳實踐建議或解決方案,目前爲止,還沒有找到行業內的公認解決方案,可能緣由是Android6.0以前,市面上主要互聯網公司的產品早已面世,如今無非是遷移到新的系統版本而已,徹底基於Android6.0app或新產品不多。各App廠商或多或少都會遇到Binder問題,取決於手機硬件環境和手機廠商對android系統的定製深度。我感受各App廠商如今都在尋求一種解決Binder崩潰的方案,只不過在大多數App廠商來看,Binder問題崩潰的狀況較少,其涉及模塊和緣由比較複雜,和系統軟硬件有關,因此他們暫時將Binder問題納爲待研究問題狀態,能夠暫時容忍少許的App崩潰現像,由於不像白牌項目受到客戶的嚴格穩定性指標所限。

相關文章
相關標籤/搜索