Android Binder綱要

         Binder是Android系統中進程間通信(IPC)的一種方式,也是Android系統中最重要的特性之一。Android中的四大組件Activity,Service,Broadcast,ContentProvider,不一樣的App等都運行在不一樣的進程中,它是這些進程間通信的橋樑。正如其名「粘合劑」同樣,它把系統中各個組件粘合到了一塊兒,是各個組件的橋樑。Binder承擔了絕大部分Android進程通訊的職責,能夠看作是Android的血管系統,負責不一樣服務模塊進程間的通訊。在對Binder的理解上,可大可小,平常APP開發並不怎麼涉及Binder通訊知識,最多就是Service及AIDL的使用會涉及部分Binder知識。Binder往小了說可總結成一句話:一種IPC進程間通訊方式,負責進程A的數據,發送到進程B。往大了說,其實涉及的知識仍是不少的,如Android 對於原Binder驅動的擴展、Zygote進程孵化中對於Binder通訊的支持、Java層Binder封裝,Native層對於Binder通訊的封裝、Binder訃告機制等等。


Binder 架構 

 Binder 通訊採用 C/S 架構,從組件視角來講,包含 Client、 Server、 ServiceManager 以及 Binder 驅動,其中 ServiceManager 用於管理系統中的各類服務。程序員

 Binder 在 framework 層進行了封裝,經過 JNI 技術調用 Native(C/C++)層的 Binder 架構。 緩存

Binder 在 Native 層以 ioctl 的方式與 Binder 驅動通信。架構


Binder 機制

       首先須要註冊服務端,只有註冊了服務端,客戶端纔有通信的目標,服務端經過 ServiceManager 註冊服務,註冊的過程就是向 Binder 驅動的全局鏈表 binder_procs 中插入服務端的信息(binder_proc 結構體,每一個 binder_proc 結構體中都有 todo 任務隊列),而後向 ServiceManager 的 svcinfo 列表中緩存一下注冊的服務。  併發

有了服務端,客戶端就能夠跟服務端通信了,通信以前須要先獲取到服務,拿到服務的代理,也能夠理解爲引用。 獲取服務端的方式就是經過 ServiceManager 向 svcinfo 列表中查詢一下返回服務端的代理,svcinfo 列表就是全部已註冊服務的通信錄,保存了全部註冊的服務信息。  app

有了服務端的引用咱們就能夠向服務端發送請求了,經過 BinderProxy 將咱們的請求參數發送給 ServiceManager,經過共享內存的方式使用內核方法 copy_from_user() 將咱們的參數先拷貝到內核空間,這時咱們的客戶端進入等待狀態,而後 Binder 驅動向服務端的 todo 隊列裏面插入一條事務,執行完以後把執行結果經過 copy_to_user() 將內核的結果拷貝到用戶空間(這裏只是執行了拷貝命令,並無拷貝數據,binder只進行一次拷貝),喚醒等待的客戶端並把結果響應回來,這樣就完成了一次通信 。異步

Binder 進程與線程

Binder 線程池:不管是system_server進程,仍是app進程,都是在進程fork完成後,便會在新進程中執行onZygoteInit()的過程當中,啓動binder線程池。每一個 Server 進程在啓動時建立一個 binder 線程池,並向其中註冊一個 Binder 線程;以後 Server 進程也能夠向 binder 線程池註冊新的線程,或者 Binder 驅動在探測到沒有空閒 binder 線程時主動向 Server 進程註冊新的的 binder 線程。對於一個 Server 進程有一個最大 Binder 線程數限制,默認爲16個 binder 線程,例如 Android 的 system_server 進程就存在16個線程。對於全部 Client 端進程的 binder 請求都是交由 Server 端進程的 binder 線程來處理的。     ide

 對於底層Binder驅動,經過 binder_procs 鏈表記錄全部建立的 binder_proc 結構體,binder 驅動層的每個 binder_proc 結構體都與用戶空間的一個用於 binder 通訊的進程一一對應,且每一個進程有且只有一個 ProcessState 對象,這是經過單例模式來保證的。在每一個進程中能夠有不少個線程,每一個線程對應一個 IPCThreadState 對象,IPCThreadState 對象也是單例模式,即一個線程對應一個 IPCThreadState 對象,在 Binder 驅動層也有與之相對應的結構,那就是 Binder_thread 結構體。在 binder_proc 結構體中經過成員變量 rb_root threads,來記錄當前進程內全部的 binder_thread。 函數

 


https://blog.csdn.net/freekiteyu/article/details/70082302  
oop

Binder線程是執行Binder服務的載體,只對於服務端纔有意義,對請求端來講,是不須要考慮Binder線程的,但Android系統的處理機制其實大部分是互爲C/S的。好比APP與AMS進行交互的時候,都互爲對方的C與S,這裏先不討論這個問題,先看Binder線程的概念。性能

Binder線程就是執行Binder實體業務的線程,一個普通線程如何才能成爲Binder線程呢?很簡單,只要開啓一個監聽Binder字符設備的Loop線程便可,在Android中有不少種方法,不過歸根到底都是監聽Binder,換成代碼就是經過ioctl來進行監聽。

拿ServerManager進程來講,其主線就是Binder線程,其作法是經過binder_loop實現不死線程。咱們APP的主線程,在startActivity請求AMS的時候,APP的主線程成其實就是Binder請求線程,在進行Binder通訊的過程當中,Client的Binder請求線程會一直阻塞,直到Service處理完畢返回處理結果。


Binder傳輸數據的大小限制

          雖然APP開發時候,Binder對程序員幾乎不可見,可是做爲Android的數據運輸系統,Binder的影響是全面性的,因此有時候若是不瞭解Binder的一些限制,在出現問題的時候每每是沒有任何頭緒,好比在Activity之間傳輸BitMap的時候,若是Bitmap過大,就會引發問題,好比崩潰等,這其實就跟Binder傳輸數據大小的限制有關係,在上面的一次拷貝中分析過,mmap函數會爲Binder數據傳遞映射一塊連續的虛擬地址,這塊虛擬內存空間實際上是有大小限制的,不一樣的進程可能還不同。普通的由Zygote孵化而來的用戶進程,所映射的Binder內存大小是不到1M的,這個限制定義在ProcessState類中,若是傳輸說句超過這個大小,系統就會報錯,由於Binder自己就是爲了進程間頻繁而靈活的通訊所設計的,並非爲了拷貝大數據而使用的。

Binder運用在服務上

服務可分爲系統服務與普通服務,系統服務通常是在系統啓動的時候,由SystemServer進程建立並註冊到ServiceManager中的。而普通服務通常是經過ActivityManagerService啓動的服務,或者說經過四大組件中的Service組件啓動的服務。這兩種服務在實現跟使用上是有不一樣的,主要從如下幾個方面:

  • 服務的啓動方式
  • 服務的註冊與管理
  • 服務的請求使用方式

首先看一下服務的啓動上,系統服務通常都是SystemServer進程負責啓動,好比AMS,WMS,PKMS,電源管理等,這些服務自己其實實現了Binder接口,做爲Binder實體註冊到ServiceManager中,被ServiceManager管理,而SystemServer進程裏面會啓動一些Binder線程,主要用於監聽Client的請求,並分發給響應的服務實體類,能夠看出,這些系統服務是位於SystemServer進程中(有例外,好比Media服務)。在來看一下bindService類型的服務,這類服務通常是經過Activity的startService或者其餘context的startService啓動的,這裏的Service組件只是個封裝,主要的是裏面Binder服務實體類,這個啓動過程不是ServcieManager管理的,而是經過ActivityManagerService進行管理的,同Activity管理相似。

再來看一下服務的註冊與管理:系統服務通常都是經過ServiceManager的addService進行註冊的,這些服務通常都是須要擁有特定的權限才能註冊到ServiceManager,而bindService啓動的服務能夠算是註冊到ActivityManagerService,只不過ActivityManagerService管理服務的方式同ServiceManager不同,而是採用了Activity的管理模型,詳細的能夠自行分析

最後看一下使用方式,使用系統服務通常都是經過ServiceManager的getService獲得服務的句柄,這個過程其實就是去ServiceManager中查詢註冊系統服務。而bindService啓動的服務,主要是去ActivityManagerService中去查找相應的Service組件,最終會將Service內部Binder的句柄傳給Client。


連接:https://www.jianshu.com/p/adaa1a39a274

Binder請求的同步與異步

           不少人都會說,Binder是對Client端同步,而對Service端異步,其實並不徹底正確,在單次Binder數據傳遞的過程當中,其實都是同步的。只不過,Client在請求Server端服務的過程當中,是須要返回結果的,即便是你看不到返回數據,其實仍是會有個成功與失敗的處理結果返回給Client,這就是所說的Client端是同步的。至於說服務端是異步的,能夠這麼理解:在服務端在被喚醒後,就去處理請求,處理結束後,服務端就將結果返回給正在等待的Client線程,將結果寫入到Client的內核空間後,服務端就會直接返回了,不會再等待Client端的確認,這就是所說的服務端是異步的。通常而言,Client同步阻塞請求Service,直到Service提供完服務後才返回,不過,也有特殊的,好比請求用ONE_WAY方式,這種場景通常主要是用來通知,至於通知被誰消費,是否被消費壓根不會關心。拿ContentService服務爲例子,它是一個全局的通知中心,負責轉發通知,並且,通常是羣發,因爲在轉發的時候,ContentService被看作Client,若是這個時候採用普通的同步阻塞勢必會形成通知的延時發送送,因此這裏的Client採用了oneway,異步。這種機制可能也會影響Service的性能,好比 同一個線程中的Client請求的服務是一個耗時操做的時候,經過oneway的方式發送請求的話,若是以前的請求還沒被執行完,則Service不會啓動新的線程去響應,該請求線程的全部操做都會被放到同一個Binder線程中依次執行,這樣其實沒有利用Binder機制的動態線程池,若是是多個線程中的Client併發請求,則仍是會動態增長Binder線程的,大概這個是爲了保證同一個線程中的Binder請求要依次執行吧,這種表現好像是反過來了,Client異步,而Service阻塞了,也就是說雖然解決了Client請求不被阻塞的問題,可是請求的處理並未被加速。

bindService啓動Service與Binder服務實體的流程 

      ServiceManager其實主要的面向對象是系統服務,大部分系統服務都是由SystemServer進程總添加到ServiceManager中去的,在經過ServiceManager添加服務的時候,是有些權限校驗的, 普通的進程是沒有權限註冊到ServiceManager中的,那麼APP平時經過bindService啓動的服務怎麼註冊於查詢的呢?接管這個任務的就是SystemServer的ActivityManagerService。
        bindService比startService多了一套Binder通訊,其他的流程基本相同,而startService的流程,同startActivity差很少,四大組件的啓動流程這裏不作分析點,主要看bindService中C/S通訊的創建流程,在這個流程裏面,APP與服務端互爲C/S的特性更明顯,在APP開發的時候,binder服務是經過Service來啓動的。Service的啓動方式有兩種startService,與bindService,這裏只考慮後者,另外啓動的binder服務也分爲兩種狀況:第一種,client同server位於同一進程,能夠看作內部服務,第二種,Client與Server跨進程,即便是位於同一個APP,第一種能夠不用AIDL來編寫,可是第二種必須經過AIDL實現跨進程通訊。bindService大部分的流程與startActivity相似,其實都是經過AMS啓動組件,這裏只將一些不一樣的地方,Activity啓動只須要Intent就能夠了,而Service的bind須要一個ServiceConnection對象,這個對象實際上是爲了AMS端在啓動Service後回調用的,ServiceConnection是個接口,其實例在ContextImpl的,在LoadApk中IServiceConnection對象是經過context鍵值來存儲ServiceDispatcher對象,而ServiceDispatcher對象內存會有個InnerConnection對象,該對象就是getServiceDispatcher的返回對象。所以bindServiceCommon最終調用
ActivityManagerNative.getDefault().bindService(x,x,x,x,x sd, x, x, x) 的時候,傳遞的參數sd其實就是一個InnerConnection對象,這是個Binder實體。主要是經過ApplicationThread的binder通訊通知App端啓動Service,這個流程同Activity啓動同樣。ActivityManagerNative.getDefault().publishService會將啓動的Binder服務實體傳遞給AMS,上面分析過Binder實體傳輸,這裏的原理是同樣的,AMS端在傳輸結束後,會得到Service端服務實體的引用,這個時候,就能經過最初的InnerConnection的回調將這個服務傳遞給Client端。Activity的bindService流程:

  • 一、Activity調用bindService:經過Binder通知ActivityManagerService,要啓動哪一個Service
  • 二、ActivityManagerService建立ServiceRecord,並利用ApplicationThreadProxy回調,通知APP新建並啓動Service啓動起來
  • 三、ActivityManagerService把Service啓動起來後,繼續經過ApplicationThreadProxy,通知APP,bindService,其實就是讓Service返回一個Binder對象給ActivityManagerService,以便AMS傳遞給Client
  • 四、ActivityManagerService把從Service處獲得這個Binder對象傳給Activity,這裏是經過IServiceConnection binder實現。
  • 五、Activity被喚醒後經過Binder Stub的asInterface函數將Binder轉換爲代理Proxy,完成業務代理的轉換,以後就能利用Proxy進行通訊了。
  • 相關文章
    相關標籤/搜索