Android - Binder驅動

来源:http://www.cnblogs.com/Doing-what-I-love/archive/2016/05/26/5530173.html
-Advertisement-
Play Games

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


  以下資料摘錄整理自老羅的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對象mRemotetransact成員函數是一個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線程的事務堆棧)維護):  




您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 蘋果的規定:2016年6月1日提交到App Store必須支持IPv6-only網路。 官方說明:https://developer.apple.com/library/mac/documentation/NetworkingInternetWeb/Conceptual/NetworkingOver ...
  • ARC 和 MRC 記憶體管理 從 MRC—>ARC 就是將記憶體管理部分,從開發者的函數中轉移到函數外部的runtime 中.由於 runtime 的開發簡單,邏輯層次高,所以 runtime 的出錯幾率很小,另外由於編譯器的優化,使得運行速度很高. ARC 的特點: 不能顯示的調用 dealloc, ...
  • 報錯一: 在iOS7的真機運行時,彈出錯誤:App installation failed. There was an internal API error. 如圖 解決辦法: 在Xcode -> Target -> Build Setting 下的Packaging部分,將Product Name ...
  • 1 什麼是 OC 語言? OC 語言即面向對象語言,它擴展了 ANSI C 語言,將 SmallTalk 式的消息傳遞機制加入到 ANSI C 中.它是蘋果 OS 和 iOS 以及相關的 API,Cocoa 和 Cocoa Touch 的主要編程語言. 2 OC 語言的特點: 支持 C 語言語法,是 ...
  • 今天建立android4.3模擬器的時候發現沒有android4.3 Intel X86 Atom System Image可選,打開android SDK Manager 於是希望重現選擇下載安裝,但是時間等得太久,都沒與出現可以下載安裝的按鈕 於是從下麵的網址選擇相應的版本驚進行安裝: http ...
  • 在圖示位置把提示的東西加上即可 參考: iOS app with framework crashed on device, dyld: Library not loaded, Xcode 6 Beta 但是當歸檔完成導出ipa文件時會提示"Found an unexpected Mach-O hea ...
  • 1、概述 開發Android的同學都知道sdk已經為我們提供了一個SQLiteOpenHelper類來創建和管理SQLite資料庫,通過寫一個子類去繼承它,就可以方便的創建、管理資料庫。但是當我們需要去做增刪改查的操作的時候,就得通過getWritableDatabase獲取一個SQLiteData ...
  • _introduce.text=status.introduce; //設置行間距 NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:_introduce.te ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...