Android - Binder驅動


 
以下資料摘錄整理自老羅的Android之旅博客,是對老羅的博客關於Android底層原理的一個抽象的知識概括總結(如有錯誤歡迎指出)(侵刪):
http://blog.csdn.net/luoshengyang/article/details/8923485
http://blog.csdn.net/luoshengyang/article/details/12957169
 
整理by Doing
 

Binder機制介紹

傳統的IPC ,例如Pipe和Socket,執行一次通信需要兩次數據拷貝
內存共享機制雖然只需要執行一次數據拷貝,但是它需要結合其它IPC(如:信號量)來做進程同步,效率同樣不理想
 
Binder是一種高效且易用的IPC機制,提供遠程過程調用(RPC)功能:
  • 一次數據拷貝
  • Client/Server通信模型
  • 既可用作進程間通信,也可用作進程內通信
          在Android系統的Binder機制中,由一系統組件組成,分別是Client、Server、Service Manager和Binder驅動程序( 其中Client、Server和Service Manager運行在用戶空間,Binder驅動程序運行內核空間 )。Binder就是一種把這四個組件粘合在一起的粘結劑了,其中,核心組件便是Binder驅動程序了,Service Manager提供了輔助管理的功能,Client和Server正是在Binder驅動和Service Manager提供的基礎設施上,進行Client-Server之間的通信。
          Service Manager和Binder驅動已經在Android平台中實現好,開發者只要按照規范實現自己的Client和Server組件就可以了。
  1. Client、Server和Service Manager實現在用戶空間中,Binder驅動程序實現在內核空間中
  2.  Binder驅動程序和Service Manager在Android平台中已經實現,開發者只需要在用戶空間實現自己的Client和Server
  3.  Binder驅動程序提供設備文件/dev/binder與用戶空間交互,Client、Server和Service Manager通過open和ioctl文件操作函數與Binder驅動程序進行通信
  4. Client和Server之間的進程間通信通過Binder驅動程序間接實現
  5. Service Manager是一個守護進程,用來管理Server,並向Client提供查詢Server接口的能力
 

一次數據拷貝

Binder驅動為每一個進程分配4M的內核緩沖區(物理頁面,可以是不連續),用作數據傳輸:
  • 4M內核緩沖區所對應的物理頁面除了映射在內核空間之外,還會被映射在進程的用戶空間(同時使用進程虛擬地址空間和內核虛擬地址空間來映射同一個物理頁面
  • 4M內核緩沖區所對應的物理頁面是按需要分配的,一開始只有一個物理頁被被映射
  • 用戶空間:0G ~ 3G
  • 內核空間:3~ 4G。其中,3G ~ (3G + 896M)范圍的地址是用來映射連續的物理頁面的,這個范圍的虛擬地址和對應的實際物理地址有着簡單的對應關系,即對應0~896M的物理地址空間;而(3G + 896M) ~ (3G + 896M + 8M)是安全保護區域(例如,所有指向這8M地址空間的指針都是非法的);因此使用(3G + 896M + 8M) ~ 4G地址空間來映射非連續的物理頁面
 
進程間的一次數據拷貝:
          Binder進程間通信機制的精髓所在,同一個物理頁面,一方映射到進程虛擬地址空間,一方面映射到內核虛擬地址空間,這樣,進程和內核之間就可以減少一次內存拷貝了,提到了進程間通信效率。舉個例子如,Client要將一塊內存數據傳遞給Server,一般的做法是,Client將這塊數據從它的進程空間拷貝到內核空間中,然后內核再將這個數據從內核空間拷貝到Server的進程空間,這樣,Server就可以訪問這個數據了,但是在這種方法中,執行了兩次內存拷貝操作,而采用我們Binder機制,只需要把Client進程空間的數據拷貝一次到內核空間,然后Server與內核共享這個數據就可以了,整個過程只需要執行一次內存拷貝,提高了效率。
 

Client/Server通信模型

Server進程啟動時,將在本進程內運行的Service注冊到Service Manager中,並且啟動一個Binder線程池,用來接收Client進程請求
Client進程向Service Manager查詢所需要的Service,並且獲得一個Binder代理對象,通過該代理對象即可向Service發送請求
 
句柄(handle)
在程序設計中,句柄(handle)是一種特殊的智能指針。當一個應用程序要引用 其他系統(如數據庫、操作系統)所管理的內存塊或對象時,就要使用句柄。        句柄與普通指針的區別在於,指針包含的是引用對象的內存地址,而句柄則是由系統所管理的引用標識,該標識可以被系統重新定位到一個內存地址上。這種間接訪問對象的模式增強了系統對引用對象的控制 (封裝) 。通俗的說就是我們調用句柄就是調用句柄所提供的服務,即句柄已經把它能做的操作都設定好了,我們只能在句柄所提供的操作范圍內進行操作,但是普通指針的操作卻多種多樣,不受限制。
Service Manager注冊及其代理獲得
(Service Manager:一個特殊Service,它的代理句柄值永遠等於0)
Service Manager是成為Android進程間通信(IPC)機制Binder守護進程的過程:
  1. 打開/dev/binder文件:open("/dev/binder", O_RDWR);
  2.  建立128K內存映射:mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
  3.  通知Binder驅動程序它是守護進程:binder_become_context_manager(bs);
  4. 進入循環等待請求的到來:binder_loop(bs, svcmgr_handler);
 
 
Service代理獲取
             對於普通的Server來說,Client如果想要獲得Server的遠程接口,那么必須通過Service Manager遠程接口提供的getService接口來獲得,這本身就是一個使用Binder機制來進行進程間通信的過程。
          而對於Service Manager這個Server來說,Client如果想要獲得Service Manager遠程接口,卻不必通過進程間通信機制來獲得,因為Service Manager遠程接口是一個特殊的Binder引用,它的引用句柄一定是0。
 
 
Service注冊
    Server獲得了Service Manager遠程接口之后,把自己的Service添加到Service Manager中去,然后把自己啟動起來,在一個無窮循環中等待Client的請求。
 
 
Client和Server的通信過程
 
 
Binder進程間通信機制的Java接口
  1. 每一個Java層的Binder本地對象(Binder)在C++層都對應有一個JavaBBinder對象,后者是從C++層的BBinder繼承下來的
  2. 每一個Java層的Binder代理對象(BinderProxy)在C++層都對應有一個BpBinder對象
  3. 於是Java層的Binder進程間通信實際上就是通過C++層的BpBinder和BBinder來進行的,與C++層的Binder進程間通信 一致
          Binder機制在應用程序框架層中的Java接口,主要就是Service Manager、Server和Client這三個角色的實現了。通常,在應用程序中,我們都是把Server實現為Service的形式,並且通過IServiceManager.addService接口來把這個Service添加到Service ManagerClient也是通過IServiceManager.getService接口來獲得Service接口,接着就可以使用這個Service提供的功能了,這個與運行時庫的Binder接口是一致的。(XXX.AIDL自動生成的IXXX.java類中的Proxy內部類的Binder對象( mRemote) 實際上是一個BinderProxy對象,即對應於C++層的 BpBinder對象 mRemote transact成員函數是一個JNI方法
 
 

Binder對象

Binder對象(flat_binder_object)的類型:
BINDER_TYPE_BINDERBINDER_TYPE_WEAK_BINDER類型的flat_binder_object傳輸發生在:
  • Server進程主動向Client進程發送Service (匿名Service )
  • Server進程向Service Manager進程注冊Service
BINDER_TYPE_HANDLEBINDER_TYPE_WEAK_HANDLE類型的flat_binder_object傳輸發生在:
  • 一個Client向另外一個進程發送Service代理
BINDER_TYPE_FD類型的flat_binder_object傳輸發生在:
  • 一個進程向另外一個進程發送文件描述符
Binder對象的引用計數
  • BBinder:位於用戶空間,通過智能指針管理,有mStrong和mWeak兩個引用計數
  • BpBinder:位於用戶空間,通過智能指針管理,有mStrong和mWeak兩個引用計數
  • binder_node:位於內核空間,有internal_strong_refs、local_weak_refs和local_strong_refs三個引用計數,以及一個binder_ref引用列表refs
  • binder_ref:位於內核空間,有strong和weak兩個引用計數
Binder對象之間的引用關系
  • Binder對象引用關系小結:
    - BBinder被binder_node引用
    - binder_node被binder_ref引用
    - binder_ref被BpBinder引用
    - BBinder和BpBinder運行在用戶空間
    - binder_node和binder_ref運行在內核空間
  • 內核空間足夠健壯,保證binder_node和binder_ref不會異常銷毀
  • 用戶空間不夠健壯,BBinder和BpBinder可能會異常銷毀
  • BpBinder異常銷毀不會引發致命問題,但是BBinder異常銷毀會引發致命問題
  • 需要有一種方式來監控BBinder和BpBinder的異常銷毀:
Binder對象異常銷毀監控
  1. 所有執行Binder IPC的進程都需要打開/dev/binder文件
  2. 進程異常退出的時候,內核保證會釋放未正常關閉的它打開的/dev/binder文件,即調用與/dev/binder文件所關聯的release回調函數
  3. Binder驅動通過實現/dev/binder文件的release回調函數即可監控Binder對象的異常銷毀,進而執行清理工作
BBinder異常銷毀的時候,不單止Binder驅動需要執行清理工作,引用了它的BpBinder所在的Client進程也需要執行清理工作,所以需要有一種 BBinder死亡通知機制
  1. Client進程從Binder驅動獲得一個BpBinder
  2. Client進程向Binder驅動注冊一個死亡通知,該死亡通知與上述BpBinder所引用的BBinder相關聯
  3. Binder驅動監控到BBinder所在進程異常退出的時候,檢查該BBinder是否注冊有死亡通知
  4. Binder驅動向注冊的死亡通知所關聯的BpBinder所運行在的Client進程發送通知
  5. Client進程執行相應的清理工作
 
Binder驅動對 Binder對象(flat_binder_object)的 處理
源進程:client目標進程:server
Binder驅動對類型 BINDER_TYPE_BINDER 的Binder對象(flat_binder_object)的 處理:
  1. 在源進程中找到對應的binder_node。如果不存在,則創建。
  2. 根據上述binder_node在目標進程中找到對應的binder_ref。如果不存在,則創建。
  3. 增加上述binder_ref的強引用計數和弱引用計數(智能指針)
  4. 構造一個類型為BINDER_TYPE_HANDLE的flat_binder_object對象。
  5. 將上述flat_binder_object對象發送給目標進程。
 
Binder驅動對類型 BINDER_TYPE_WEAK_BINDER 的Binder對象(flat_binder_object)的 處理:
  1. 在源進程中找到對應的binder_node。如果不存在,則創建。
  2. 根據上述binder_node在目標進程中找到對應的binder_ref。如果不存在,則創建。
  3. 增加上述binder_ref的弱引用計數。(智能指針)
  4. 構造一個類型為BINDER_TYPE_WEAK_HANDLE的flat_binder_object對象。
  5. 將上述flat_binder_object對象發送給目標進程。
 
Binder驅動對類型 BINDER_TYPE_HANDLE 的Binder對象(flat_binder_object)的 處理:
- 在源進程中找到對應的binder_ref。
  • 如果上述binder_ref所引用的binder_node所在進程就是目標進程:
  1. 增加上述binder_node的強引用計數和弱引用計數(智能指針)
  2. 構造一個類型為BINDER_TYPE_BINDER的flat_binder_object
  3. 將上述flat_binder_object發送給目標進程
  • 如果上述binder_ref所引用的binder_node所在進程不是目標進程:
  1. 為目標進程創建一個binder_ref,該binder_ref與上述binder_ref引用的是同一個binder_node
  2. 增加上述新創建的binder_ref的強引用計數和弱引用計數(智能指針)
  3. 構造一個類型為BINDER_TYPE_HANDLE的flat_binder_object
  4. 將上述flat_binder_object發送給目標進程
 
Binder驅動對類型 BINDER_TYPE_WEAK_HANDLE 的Binder對象(flat_binder_object)的 處理:
- 在源進程中找到對應的binder_ref。
  • 如果上述binder_ref所引用的binder_node所在進程就是目標進程:
  1. 增加上述binder_node的弱引用計數(智能指針)
  2. 構造一個類型為BINDER_TYPE_WEAK_BINDER的flat_binder_object
  3. 將上述flat_binder_object發送給目標進程
  • 如果上述binder_ref所引用的binder_node所在進程不是目標進程:
  1. 為目標進程創建一個binder_ref,該binder_ref與上述binder_ref引用的是同一個binder_node
  2. 增加上述新創建的binder_ref的弱引用計數(智能指針)
  3. 構造一個類型為BINDER_TYPE_WEAK_HANDLE的flat_binder_object
  4. 將上述flat_binder_object發送給目標進程
 
Binder驅動對類型 BINDER_TYPE_FD 的Binder對象(flat_binder_object)的 處理:
  1. 在源進程中找到對應的struct file結構體
  2. 將上述struct file結構體 保存在目標進程的打開文件列表中
  3. 構造一個類型為BINDER_TYPE_FD的flat_binder_object
  4. 將上述flat_binder_object發送給目標進程
 

Binder線程池

  1. 每一個Server進程在啟動的時候都會創建一個Binder線程池,並且向里面注冊一個Binder線程
  2. 之后Server進程可以無限地向Binder線程池注冊新的Binder線程
  3. Binder驅動發現Server進程沒有空間的Binder線程時,會主動向Server進程請求注冊新的Binder線程(Binder驅動主動請求Server進程注冊新的Binder線程的數量可以由Server進程設置,默認是16
  4. 在Server進程中,Client進程發送過來的Binder請求由Binder線程進行處理
 
Binder線程調度機制
  1. 在Binder驅動中,每一個Server進程都有一個todo list,用來保存Client進程發送過來的請求,這些請求可以由其Binder線程池中的任意一個空閑線程處理
  2. 在Binder驅動中,每一個Binder線程也有一個todo list,用來保存Client進程發送過來的請求,這些請求只可以由該Binder線程處理
  3. Binder線程沒事做的時候,就睡眠在Binder驅動中,直至它所屬的Server進程的todo list或者它自己的todo list有新的請求為止
  4. 每當Binder驅動將Client進程發送過來的請求保存在Server進程的todo list時,都會喚醒其Binder線程池的空閑Binder線程池,並且讓其中一個來處理
  5. 每當Binder驅動將Client進程發送過來的請求保存在Binder線程的todo list時,都會將其喚醒來處理
 
同步請求優先於異步請求
  1. 同一時刻,一個BBinder只能處理一個異步請求
  2. 第一個異步請求將被保存在目標進程(server進程)的todo list
  3. 第一個異步請求未被處理前,其它的異步請求將被保存在對應的 binder_node的async todo list
  4. 第一個異步請求被處理之后,第二個異步請求將從binder_node的async todo list轉移至目標進程的todo list等待處理
  5. 依次類推……
此外,所有同時進行的異步請求所占用的內核緩沖區大小不超過目標進程的總內核緩沖區大小的一半
 
與請求相關的三個線程優先級:
  1. 源線程的優先級
  2. 目標線程的優先級
  3. 目標binder_node的最小優先級(注冊Service時設置)
  • Binder線程處理同步請求時的優先級:取{源線程,目標binder_node,目標線程}的最高優先級;保證目標線程以不低於源線程優先級或者binder_node最小優先級的優先級運行
  • Binder線程處理異步請求時的優先級:取{目標binder_node,目標線程}的最高優先級;保證目標線程以不低於binder_node最小優先級的優先級運行
 
Binder線程的事務(binder_transaction)堆棧
默認情況下, Client進程發送過來的請求都是保存在Server 進程的todo list中。
然而有一種特殊情況:
  1.     -源進程P1的線程A向目標進程發起了一個請求T1,該請求被分發給目標進程P2的線程B處理
  2.     -源進程P1的線程A等待目標進程P2的線程B處理處理完成請求T1
  3.     -目標進程P2的線程B在處理請求T1的過程中,需要向源進程P1發起另一個請求T2
  4.     -源進程P1除了線程A是處於空閑等待狀態之外,還有另外一個線程C處於空閑等待狀態
這時候請求T2應該分發給線程A處理,還是線程C處理?
  •     -如果分發給線程C處理,則線程A仍然是處於空閑等待狀態
  •     -如果分發給線程A處理,則線程 C可以處理其它新的請求
所以應該分發給A(通過binder_transaction的堆棧( Binder線程的事務堆棧 )維護):
 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM