Binder实用指南(一) - 理解篇
這是關于Android Binder機制的一篇文章,Binder是Android里面非常重要的組成,也是最難理解的一塊知識點,學習Binder最好的方法是深入源碼閱讀,因為Binder相關的知識錯綜復雜,一般初學者也很容易迷失在源碼的汪洋里,本文旨在梳理Binder的架構和流程,并且試著以實用的角度來看待Binder。
一、為什么需要Binder機制?
Android系統中,每個應用程序是由Android的Activity,Service,Broadcast,ContentProvider這四劍客的中一個或多個組合而成,這四劍客所涉及的多進程間的通信底層都是依賴于Binder IPC機制。例如當進程A中的Activity要向進程B中的Service通信,這便需要依賴于Binder IPC。
如果熟悉Android源碼,其實可以知道整個Android系統架構中,也大量采用了Binder機制作為IPC(進程間通信)方案。
Android是在Linux內核的基礎上設計的。而在Linux中,已經擁有”管道/消息隊列/共享內存/信號量/Socket等等”眾多的IPC通信手段;但是,Google為什么單單選擇了Binder,可見Binder肯定有自己獨特的優勢:
1.1 Binder能很好的實現C/S架構
Android系統,很大一部分都是居于Client-Server架構的設計。Client端有什么需求,直接發送給Server端去完成,Server處理完畢再將反饋內容發送給Client。Server端與Client端相對獨立,穩定性較好。傳統的CS架構只有Socket,但是Socket通信效率相對于其他IPC來說又太低效,而Binder正是基于C/S架構設計的。
1.2 Binder傳輸效率高
Binder只需要進行一次拷貝,把Client端的用戶空間的數據即copy_from_user()到內核空間,然后將內核空間的數據映射到Server端的用戶空間。
Binder性能上僅僅次于Linux 共享內存的方式,但是共享內存的方式,進程間同步又是一個難題。
1.3 Binder安全性極高
Android為每個安裝好的應用程序分配了自己的UID,故進程的UID是鑒別進程身份的重要標志,Client端將任務發送給Server端,Server端會根據權限控制策略,判斷UID/PID是否滿足訪問權限。
Client-Server通信過程中,Binder內核會為每個Client進程分配了UID/PID來作為鑒別身份的標示,并且在Binder通信時會根據UID/PID進行有效性檢測。
而如果是傳統的IPC只能由在數據包當中填入UID/PID,這個并不是一個可靠的方法。
知乎上有一位答主講得很好,可以看看:
為什么 Android 要采用 Binder 作為 IPC 機制?
二、Binder原理
三、Binder驅動層
當用戶空間調用open()方法,最終會調用binder驅動的binder_open()方法;mmap()/ioctl()方法也是同理,從用戶態進入內核態,都依賴于系統調用過程。
3.1 binder_init
注冊misc設備,指定相應文件操作的方法。
3.2 binder_open
創建binder_proc對象,并把當前進程等信息保存到binder_proc對象,該對象管理IPC所需的各種信息并擁有其他結構體的根結構體;再把binder_proc對象保存到文件指針filp,以及把binder_proc加入到全局鏈表binder_procs。
3.3 binder_mmap
在內核虛擬地址空間,申請一塊與用戶虛擬內存相同大小的內存;然后再申請1個page大小的物理內存,再將同一塊物理內存分別映射到內核虛擬地址空間和用戶虛擬內存空間,從而實現了用戶空間的Buffer和內核空間的Buffer同步操作的功能。
3.4 binder_ioctl
負責在兩個進程間收發IPC數據和IPC reply數據。調用流程比如:
| 123456789101112 | //step 1:binder_write_read bwr;ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) // step 2:binder_ioctl(filp, BINDER_WRITE_READ, &bwr) // step 3:binder_ioctl_write_read(filp, BINDER_WRITE_READ, &bwr, thread) // step 4:copy_from_user(&bwr, ubuf, sizeof(bwr))binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);copy_to_user(...) |
binder_thread_write():處理Binder請求碼,以”BC_“開頭,簡稱BC碼,用于從IPC層傳遞到Binder Driver層;
binder_thread_read():生成Binder響應碼,以”BR_“開頭,簡稱BR碼,用于從Binder Driver層傳遞到IPC層;
四、Binder通信流程
例如當名為BatteryStatsService的Client向ServiceManager注冊服務的過程中,IPC層的數據組成為:
Handle=0,RPC代碼為ADD_SERVICE_TRANSACTION,RPC數據為BatteryStatsService,Binder協議為BC_TRANSACTION。
整個流程圖大致如下:
handle為0正是指向ServiceManager。
五、啟動ServiceManager
ServiceManager啟動時序圖:
六、獲取ServiceManager
獲取Service Manager是通過defaultServiceManager()方法來完成,當進程注冊服務(addService)或 獲取服務(getService)的過程之前,都需要先調用defaultServiceManager()方法來獲取gDefaultServiceManager對象。
七、addService
以Native層的服務以media服務為例,注冊MediaPlayerService的時序圖如下:
獲取服務的流程基本也是差不多的,不再累述。
八、Binder架構
binder在framework層,采用JNI技術來調用native(C/C++)層的binder架構,從而為上層應用程序提供服務。 我們知道native層中,binder是C/S架構,分為Bn端(Server)和Bp端(Client)。對于java層在命名與架構上非常相近,同樣實現了一套IPC通信架構。
1.BinderProxy類代碼Client端,Binder類代表Server端
2.framework層的Binder邏輯是建立在Native層架構基礎之上的,核心邏輯都是交予Native層方法來處理
比如addService流程:
1.java層通過getIServiceManager獲得ServiceManagerProxy對象,通過該對象的BinderProxy,最終會調用BpBinder對象,由BpBinder來完成通信。
2.Binder驅動將Client端的請求轉發給BBinder的transact方法,然后由其子類JavaBBinder調用。后者會調用指定Service的方法,并返回給驅動。
九、Binder類圖
9.1 Native Binder類圖
9.2 Framework Binder類圖
十、Binder其他
介紹一些Binder其他比較重要的點,方便理清Binder的一些疑問。比如Binder實體和引用,比如ProcessState和IPCThreadState,比如數據結構怎么傳遞等。
10.1 Binder中各個角色的關系
1. Binder實體 : binder_node
Binder實體,是各個Server以及ServiceManager在內核中的存在形式。
Binder實體實際上是內核中?binder_node?結構體的對象,它的作用是在內核中保存Server和ServiceManager的信息(例如,Binder實體中保存了Server對象在用戶空間的地址)。簡言之,Binder實體是Server在Binder驅動中的存在形式,內核通過Binder實體可以找到用戶空間的Server對象。
在上圖中,Server和ServiceManager在Binder驅動中都對應的存在一個Binder實體。
2. Binder引用 : binder_ref
所謂Binder引用,實際上是內核中binder_ref結構體的對象,它的作用是在表示”Binder實體”的引用。換句話說,每一個Binder引用都是某一個Binder實體的引用,通過Binder引用可以在內核中找到它對應的Binder實體。
如果將Server看作是Binder實體的話,那么Client就好比Binder引用。Client要和Server通信,它就是通過保存一個Server對象的Binder引用,再通過該Binder引用在內核中找到對應的Binder實體,進而找到Server對象,然后將通信內容發送給Server對象。
Binder實體和Binder引用都是內核(即Binder驅動)中的數據結構。每一個Server在內核中就表現為一個Binder實體,而每一個Client則表現為一個Binder引用。這樣,每個Binder引用都對應一個Binder實體,而每個Binder實體則可以多個Binder引用。
3. 遠程服務
Server都是以服務的形式注冊到ServiceManager中進行管理的。如果將Server本身看作是”本地服務”的話,那么Client中的”遠程服務”就是本地服務的代理。如果你對代理模式比較熟悉的話,就很容易理解了,遠程服務就是本地服務的一個代理,通過該遠程服務Client就能和Server進行通信。
10.2 進程和線程的關系
圖解:
1.Binder驅動通過binder_procs鏈表記錄所有創建的binder_proc結構體,binder驅動層的每一個binder_proc結構體都與用戶空間的一個用于binder通信的進程一一對應。
2.每個進程有且只有一個ProcessState對象,這是通過單例模式來保證的。
3.每個進程中可以有很多個線程,每個線程對應一個IPCThreadState對象,IPCThreadState對象也是單例模式,即一個線程對應一個IPCThreadState對象,在Binder驅動層也有與之相對應的結構,那就是Binder_thread結構體。在binder_proc結構體中通過成員變量rb_root threads,來記錄當前進程內所有的binder_thread。
Binder線程池:
每個Server進程在啟動時會創建一個binder線程池,并向其中注冊一個Binder線程;之后Server進程也可以向binder線程池注冊新的線程,或者Binder驅動在探測到沒有空閑binder線程時會主動向Server進程注冊新的的binder線程。對于一個Server進程有一個最大Binder線程數限制,默認為16個binder線程,例如Android的system_server進程就存在16個線程。對于所有Client端進程的binder請求都是交由Server端進程的binder線程來處理的。
10.3 Binder數據傳輸
當Client向Server發送請求時,Client會將數據打包成上述格式,然后通過ioctl()發送給Binder驅動。
十一、源碼目錄
從上之下, 整個Binder架構所涉及的總共有以下5個目錄:
| 12345 | /framework/base/core/java/ (Java)/framework/base/core/jni/ (JNI)/framework/native/libs/binder (Native)/framework/native/cmds/servicemanager/ (Native)/kernel/drivers/staging/android (Driver) |
11.1 Java framework
| 12345678910111213 | /framework/base/core/java/android/os/ - IInterface.java- IBinder.java- Parcel.java- IServiceManager.java- ServiceManager.java- ServiceManagerNative.java- Binder.java /framework/base/core/jni/ - android_os_Parcel.cpp- AndroidRuntime.cpp- android_util_Binder.cpp (核心類) |
11.2 Native framework
| 1234567891011121314 | /framework/native/libs/binder - IServiceManager.cpp- BpBinder.cpp- Binder.cpp- IPCThreadState.cpp (核心類)- ProcessState.cpp (核心類)/framework/native/include/binder/ - IServiceManager.h- IInterface.h/framework/native/cmds/servicemanager/ - service_manager.c- binder.c |
11.3 Kernel
| 123 | /kernel/drivers/staging/android/- binder.c- uapi/binder.h |
總結
以上是生活随笔為你收集整理的Binder实用指南(一) - 理解篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个关于binder的debug技巧
- 下一篇: Binder实用指南(二) - 实战篇