以下資料摘錄整理自老羅的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通信模型
- 既可用作進程間通信,也可用作進程內通信
- Client、Server和Service Manager實現在用戶空間中,Binder驅動程式實現在內核空間中
- Binder驅動程式和Service Manager在Android平臺中已經實現,開發者只需要在用戶空間實現自己的Client和Server
- Binder驅動程式提供設備文件/dev/binder與用戶空間交互,Client、Server和Service Manager通過open和ioctl文件操作函數與Binder驅動程式進行通信
- Client和Server之間的進程間通信通過Binder驅動程式間接實現
- Service Manager是一個守護進程,用來管理Server,並向Client提供查詢Server介面的能力
一次數據拷貝
Binder驅動為每一個進程分配4M的內核緩衝區(物理頁面,可以是不連續),用作數據傳輸:- 4M內核緩衝區所對應的物理頁面除了映射在內核空間之外,還會被映射在進程的用戶空間(同時使用進程虛擬地址空間和內核虛擬地址空間來映射同一個物理頁面)
- 4M內核緩衝區所對應的物理頁面是按需要分配的,一開始只有一個物理頁被被映射
- 用戶空間:0G ~ 3G
- 內核空間:3G ~ 4G。其中,3G ~ (3G + 896M)範圍的地址是用來映射連續的物理頁面的,這個範圍的虛擬地址和對應的實際物理地址有著簡單的對應關係,即對應0~896M的物理地址空間;而(3G + 896M) ~ (3G + 896M + 8M)是安全保護區域(例如,所有指向這8M地址空間的指針都是非法的);因此使用(3G + 896M + 8M) ~ 4G地址空間來映射非連續的物理頁面
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守護進程的過程:- 打開/dev/binder文件:open("/dev/binder", O_RDWR);
- 建立128K記憶體映射:mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
- 通知Binder驅動程式它是守護進程:binder_become_context_manager(bs);
- 進入迴圈等待請求的到來: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介面
- 每一個Java層的Binder本地對象(Binder)在C++層都對應有一個JavaBBinder對象,後者是從C++層的BBinder繼承下來的
- 每一個Java層的Binder代理對象(BinderProxy)在C++層都對應有一個BpBinder對象
- 於是Java層的Binder進程間通信實際上就是通過C++層的BpBinder和BBinder來進行的,與C++層的Binder進程間通信 一致
Binder對象
Binder對象(flat_binder_object)的類型: BINDER_TYPE_BINDER和BINDER_TYPE_WEAK_BINDER類型的flat_binder_object傳輸發生在:- Server進程主動向Client進程發送Service (匿名Service )
- Server進程向Service Manager進程註冊Service
- 一個Client向另外一個進程發送Service代理
- 一個進程向另外一個進程發送文件描述符
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_node和binder_ref運行在內核空間
- 內核空間足夠健壯,保證binder_node和binder_ref不會異常銷毀
- 用戶空間不夠健壯,BBinder和BpBinder可能會異常銷毀
- BpBinder異常銷毀不會引發致命問題,但是BBinder異常銷毀會引發致命問題
- 需要有一種方式來監控BBinder和BpBinder的異常銷毀:
- 所有執行Binder IPC的進程都需要打開/dev/binder文件
- 進程異常退出的時候,內核保證會釋放未正常關閉的它打開的/dev/binder文件,即調用與/dev/binder文件所關聯的release回調函數
- Binder驅動通過實現/dev/binder文件的release回調函數即可監控Binder對象的異常銷毀,進而執行清理工作
- Client進程從Binder驅動獲得一個BpBinder
- Client進程向Binder驅動註冊一個死亡通知,該死亡通知與上述BpBinder所引用的BBinder相關聯
- Binder驅動監控到BBinder所在進程異常退出的時候,檢查該BBinder是否註冊有死亡通知
- Binder驅動向註冊的死亡通知所關聯的BpBinder所運行在的Client進程發送通知
- Client進程執行相應的清理工作
Binder驅動對類型BINDER_TYPE_BINDER 的Binder對象(flat_binder_object)的 處理:
- 在源進程中找到對應的binder_node。如果不存在,則創建。
- 根據上述binder_node在目標進程中找到對應的binder_ref。如果不存在,則創建。
- 增加上述binder_ref的強引用計數和弱引用計數(智能指針)
- 構造一個類型為BINDER_TYPE_HANDLE的flat_binder_object對象。
- 將上述flat_binder_object對象發送給目標進程。
- 在源進程中找到對應的binder_node。如果不存在,則創建。
- 根據上述binder_node在目標進程中找到對應的binder_ref。如果不存在,則創建。
- 增加上述binder_ref的弱引用計數。(智能指針)
- 構造一個類型為BINDER_TYPE_WEAK_HANDLE的flat_binder_object對象。
- 將上述flat_binder_object對象發送給目標進程。
- 如果上述binder_ref所引用的binder_node所在進程就是目標進程:
- 增加上述binder_node的強引用計數和弱引用計數(智能指針)
- 構造一個類型為BINDER_TYPE_BINDER的flat_binder_object
- 將上述flat_binder_object發送給目標進程
- 如果上述binder_ref所引用的binder_node所在進程不是目標進程:
- 為目標進程創建一個binder_ref,該binder_ref與上述binder_ref引用的是同一個binder_node
- 增加上述新創建的binder_ref的強引用計數和弱引用計數(智能指針)
- 構造一個類型為BINDER_TYPE_HANDLE的flat_binder_object
- 將上述flat_binder_object發送給目標進程
- 如果上述binder_ref所引用的binder_node所在進程就是目標進程:
- 增加上述binder_node的弱引用計數(智能指針)
- 構造一個類型為BINDER_TYPE_WEAK_BINDER的flat_binder_object
- 將上述flat_binder_object發送給目標進程
- 如果上述binder_ref所引用的binder_node所在進程不是目標進程:
- 為目標進程創建一個binder_ref,該binder_ref與上述binder_ref引用的是同一個binder_node
- 增加上述新創建的binder_ref的弱引用計數(智能指針)
- 構造一個類型為BINDER_TYPE_WEAK_HANDLE的flat_binder_object
- 將上述flat_binder_object發送給目標進程
- 在源進程中找到對應的struct file結構體
- 將上述struct file結構體 保存在目標進程的打開文件列表中
- 構造一個類型為BINDER_TYPE_FD的flat_binder_object
- 將上述flat_binder_object發送給目標進程
Binder線程池
- 每一個Server進程在啟動的時候都會創建一個Binder線程池,並且向裡面註冊一個Binder線程
- 之後Server進程可以無限地向Binder線程池註冊新的Binder線程
- Binder驅動發現Server進程沒有空間的Binder線程時,會主動向Server進程請求註冊新的Binder線程(Binder驅動主動請求Server進程註冊新的Binder線程的數量可以由Server進程設置,預設是16)
- 在Server進程中,Client進程發送過來的Binder請求由Binder線程進行處理
Binder線程調度機制
- 在Binder驅動中,每一個Server進程都有一個todo list,用來保存Client進程發送過來的請求,這些請求可以由其Binder線程池中的任意一個空閑線程處理
- 在Binder驅動中,每一個Binder線程也有一個todo list,用來保存Client進程發送過來的請求,這些請求只可以由該Binder線程處理
- Binder線程沒事做的時候,就睡眠在Binder驅動中,直至它所屬的Server進程的todo list或者它自己的todo list有新的請求為止
- 每當Binder驅動將Client進程發送過來的請求保存在Server進程的todo list時,都會喚醒其Binder線程池的空閑Binder線程池,並且讓其中一個來處理
- 每當Binder驅動將Client進程發送過來的請求保存在Binder線程的todo list時,都會將其喚醒來處理
同步請求優先於非同步請求
- 同一時刻,一個BBinder只能處理一個非同步請求
- 第一個非同步請求將被保存在目標進程(server進程)的todo list中
- 第一個非同步請求未被處理前,其它的非同步請求將被保存在對應的 binder_node的async todo list中
- 第一個非同步請求被處理之後,第二個非同步請求將從binder_node的async todo list轉移至目標進程的todo list等待處理
- 依次類推……
- 源線程的優先順序
- 目標線程的優先順序
- 目標binder_node的最小優先順序(註冊Service時設置)
- Binder線程處理同步請求時的優先順序:取{源線程,目標binder_node,目標線程}的最高優先順序;保證目標線程以不低於源線程優先順序或者binder_node最小優先順序的優先順序運行
- Binder線程處理非同步請求時的優先順序:取{目標binder_node,目標線程}的最高優先順序;保證目標線程以不低於binder_node最小優先順序的優先順序運行
Binder線程的事務(binder_transaction)堆棧
預設情況下, Client進程發送過來的請求都是保存在Server 進程的todo list中。 然而有一種特殊情況:- -源進程P1的線程A向目標進程發起了一個請求T1,該請求被分發給目標進程P2的線程B處理
- -源進程P1的線程A等待目標進程P2的線程B處理處理完成請求T1
- -目標進程P2的線程B在處理請求T1的過程中,需要向源進程P1發起另一個請求T2
- -源進程P1除了線程A是處於空閑等待狀態之外,還有另外一個線程C處於空閑等待狀態
- -如果分發給線程C處理,則線程A仍然是處於空閑等待狀態
- -如果分發給線程A處理,則線程 C可以處理其它新的請求