本篇文章是《深入理解Android內(nèi)核設(shè)計思想》的讀書筆記,可能就沒書本來得詳細。
Binder分為四個部分
- Binder驅(qū)動:運行于內(nèi)核態(tài)叔遂,可以提供open(),ioctl(),mmap()等常用的文件操作
- ServiceManager:保存所有Binder Server。
- Binder Client
- Binder Service
一.Binder驅(qū)動
- 打開Binder驅(qū)動-binder-open,在里面創(chuàng)建了一個它自己的binder_proc實體
binder_proc是Binder驅(qū)動為應(yīng)用進程分配的一個數(shù)據(jù)結(jié)構(gòu)杖刷,用于存儲和該進程有關(guān)的所有信息苹粟,如內(nèi)存分配線程管理等在刺。
binder-open{
生成binder_proc對象
binder_proc初始化
把它添加到Binder的全局管理中(資源互斥添加)
}
到這里,Binder驅(qū)動已經(jīng)為用戶創(chuàng)建了一個它自己的binder_proc實體晋南,之后用戶對Binder的設(shè)備操作將以這個對象為基礎(chǔ)惠猿。
-
binder_mmap,最多支持4MB的操作
對于應(yīng)用程序而言负间,調(diào)用mmap()方法可以把設(shè)備指定的內(nèi)存塊映射到應(yīng)用程序的內(nèi)存空間中
而對于Binder驅(qū)動來說偶妖,上層用戶調(diào)用的mmap()最終就對應(yīng)了binder_mmap()對于應(yīng)用程序,它通過mmap()返回值得到一個虛擬內(nèi)存地址政溃,之后通過虛擬內(nèi)存轉(zhuǎn)換(分段分頁)最終指向物理地址某個地方
對于Binder驅(qū)動而言趾访,有個指針binder_proc->buffer指向某個虛擬內(nèi)存地址,與應(yīng)用程序指向一樣董虱,兩者共用內(nèi)存
binder_ioctl
可以替代read,write扼鞋。對應(yīng)命令 INDER_WRITE_READ,承擔Binder驅(qū)動的大部分業(yè)務(wù)小結(jié)
Binder驅(qū)動并沒有脫離Linux的典型驅(qū)動模型愤诱,它提供了多個文件操作接口mmap云头,ioctl。其binder_ioctl實現(xiàn)了應(yīng)用程序與Binder驅(qū)動之間的命令交互淫半,可以說承載了Binder驅(qū)動中的大部分業(yè)務(wù)盘寡。
二.ServiceManager - Binder Service
ServiceManager啟動
在init.rc時啟動(參考系統(tǒng)啟動章節(jié)),如果它發(fā)生重啟撮慨,其他系統(tǒng)服務(wù)zygote,media,surfaceflinger,drm也會被重新加載ServiceManager構(gòu)建
int main{
binder_open打開Binder設(shè)備,做好初始化(各種內(nèi)存地址)
binder_become_context_manager將自己設(shè)為Binder大管家脆粥,整個Android程序中只允許一個ServiceManager存在
binder_loop進入主循環(huán)
}
* binder_open初始化
> * binder_state 記錄SM中有關(guān)于Binder的所有信息
* mmap由Binder驅(qū)動決定被映射到進程空間中的內(nèi)存起始地址
* 映射區(qū)大小為128K砌溺,只讀
* 從文件的起始地址開始映射
* binder_become_context_manager初始化
> * 調(diào)用到ioctl方法
* ioctl方法中發(fā)送向Binder Driver發(fā)送 **BINDER_SET_CONTEXT_MGR**的ioctl命令使其成為大管家。(只要向Binder驅(qū)動發(fā)送BINDER_SET_CONTEXT_MGR的ioctl命令即可变隔,因為ServicerManager啟動的很早规伐,能確保它是系統(tǒng)中第一個向Binder驅(qū)動注冊成管家的程序)
* binder_loop循環(huán)等待客戶端請求
> * binder_write_read 執(zhí)行**BINDER_WRITE_READ**所需的數(shù)據(jù)格式
* 開始循環(huán)讀取消息(它的消息是從Binder驅(qū)動那里獲取的)
* 從Binder驅(qū)動讀取消息,通過**BINDER_WRITE_READ**命令
* binder_parse處理消息
* 不斷循環(huán)匣缘,而且永遠不會主動退出猖闪,除非出現(xiàn)致命錯誤
ServiceManager的功能架構(gòu)比較簡潔,內(nèi)部維護著一個svclist列表肌厨,用于存儲所有Server(Binder Service)的相關(guān)信息(以svcinfo為數(shù)據(jù)結(jié)構(gòu))培慌,查詢和注冊都是基于這個表展開
- 獲取ServiceManager服務(wù)
準確說,獲取SM服務(wù)是Binder Client需要做的工作之一柑爸。
訪問SM服務(wù)流程
1.打開Binder設(shè)置
2.執(zhí)行mmap內(nèi)存映射
3.通過Binder驅(qū)動向SM發(fā)送請求(SM的handle為0)
4.獲取結(jié)果
每個進程只允許打開一次Binder設(shè)備吵护,且只做一次內(nèi)存映射---所有需要使用Binder驅(qū)動的線程共享這一資源。
獲取SM服務(wù)流程圖
進程中ProcessState和IPCThreadState這兩個類專用與Binder驅(qū)動通信,所以Java層代碼使用Binder驅(qū)動實際上是基于他們來完成的馅而,我們稱為BpBinder祥诽。
應(yīng)用程序通過代理ServiceManagerProxy(能夠跨進程調(diào)用)來獲取到SM服務(wù),它所能提供的服務(wù)和服務(wù)端的SM必須是一致的瓮恭,也就是它也要實現(xiàn)服務(wù)端SM對應(yīng)的接口雄坪。
這里的SM指的是Binder層面的ServiceManager。
對于獲取SM服務(wù)的代理ServiceManagerProxy屯蹦,Android系統(tǒng)在ServiceManagerProxy上又加了一層封裝ServiceManager.java
2.1 獲取SM服務(wù)
首先調(diào)用ServiceManager.getService(name)
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
其中sCache用于記錄getService的歷史查詢結(jié)果维哈,加快查詢速度。如果不在則調(diào)用getIServiceManager().getService(name);
getIServiceManager
方法用于獲取一個IServiceManager
對象颇玷,而IServiceManager
接口的實現(xiàn)類則是ServiceManagerProxy
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
調(diào)用ServiceManagerNative的方法
static public IServiceManager asInterface(IBinder obj)
{
if (obj == null) {
return null;
}
IServiceManager in =
(IServiceManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ServiceManagerProxy(obj);
}
先查詢本地是否已經(jīng)有了IServiceManager
笨农,如果沒有則新建一個。
最終則是調(diào)用到了ServiceManagerProxy.getService
了
public IBinder getService(String var1) throws RemoteException {
Parcel var2 = Parcel.obtain();
Parcel var3 = Parcel.obtain();
var2.writeInterfaceToken("android.os.IServiceManager");
var2.writeString(var1);
this.mRemote.transact(1, var2, var3, 0);
IBinder var5 = var3.readStrongBinder();
var3.recycle();
var2.recycle();
return var5;
}
該函數(shù)分為以下3部分
- 準備數(shù)據(jù)帖渠。也就是通過Parcel打包數(shù)據(jù)
- IBinder.transact谒亦,利用IBinder的transact將請求發(fā)送出去,而不用理會Binder驅(qū)動的open,mmap以及一大堆具體的 Binder協(xié)議中的命令空郊。所以這個IBinder一定會在內(nèi)部使用
ProcessState
和IPCThreadState
來與Binder驅(qū)動進行通訊份招。 - 獲取結(jié)果。
上面transact后狞甚,我們就可以直接獲取到結(jié)果了锁摔。如果大家用過socket,就知道這是一種阻塞式的函數(shù)調(diào)用哼审。因為涉及到進程間通信谐腰,結(jié)果并不是馬上就能獲取到,所以Binder驅(qū)動一定要先將調(diào)用者線程掛起涩盾,直到有了結(jié)果才把它喚醒十气。
那么IBinder內(nèi)部是怎么實現(xiàn)的呢?
2.2 IBinder內(nèi)部
前面我們的IBinder對象是通過BinderInternal.getContextObject()
獲取到的
而它則是一個native方法
public static final native IBinder getContextObject();
因為和Binder驅(qū)動打交道春霍,最終都得通過JNI調(diào)用本地代碼來實現(xiàn)砸西。
而在native層中g(shù)etContextObject方法通過ProcessState
把 創(chuàng)建的對象BpBinder轉(zhuǎn)換為Java層的IBinder對象。
IBinder只是一個接口類址儒,
在native層芹枷,IBinder實現(xiàn)類為BpBinder(由ProcessState創(chuàng)建)
而在Java層,IBinder實現(xiàn)類則是BinderProxy莲趣。
前面調(diào)用IBinder.transact內(nèi)部調(diào)用流程
發(fā)現(xiàn)繞了個大圈子鸳慈,最終處理用戶的Binder請求還是通過IPCThreadState和ProcessState來實現(xiàn)的。
2.3 ProcessState
關(guān)鍵點在于
- 保證同一個進程中只有一個ProcessState實例存在妖爷,而且只有在ProcessState對象創(chuàng)建時才打開Binder設(shè)置(open_driver)以及做內(nèi)存映射(mmap)
- 向上層提供IPC服務(wù)
- 與IPCThreadState分工合作蝶涩,各司其職
可以看到ProcessState它是一個進程中的單實例理朋,而IPCThreadState則是線程中的單實例。
可以說绿聘,IPCThreadState負責與Binder驅(qū)動進行具體的命令交互嗽上,因而它的transact函數(shù)非常重要,
而ProcessState只是負責打開了Binder節(jié)點并做mmap
三.Binder Client客戶端
Binder的最大消費者是JAVA層的應(yīng)用程序熄攘。一般情況下他們可以在程序代碼的任何位置通過bindService,startActivity以及sendBroadcast等一系列接口方法來實現(xiàn)與其他進程的交互兽愤。
有了Binder驅(qū)動,Service Manager的努力挪圾,以及Android系統(tǒng)專門面向應(yīng)用開發(fā)提供的Binder強有力的封裝浅萧,才能使應(yīng)用程序之間順利地進行無縫通信。
3.1 例子bindService
應(yīng)用程序如何能依托bindService來啟動系統(tǒng)中其他進程提供的Service呢
①應(yīng)用程序填寫Intent,調(diào)用bindService發(fā)出請求
②收到請求的bindService(此時還在應(yīng)用程序的運行空間中)將與ActivityManagerService(AMS)取得聯(lián)系哲思。根據(jù)前面的分析洼畅,為了獲得AMS的Binder句柄,我們還要事先調(diào)用ServiceManager.getService棚赔,這里就涉及到進程間通信了帝簇。在得到AMS的句柄值后,程序才能真正地向它發(fā)起請求靠益。
③AMS基于特定的最優(yōu)匹配策略丧肴,從其內(nèi)部存儲的系統(tǒng)所有服務(wù)組件的資料中找到與Intent最匹配的一個,然后向它發(fā)送Service綁定請求胧后。如果目標進程還不存在的話芋浮,AMS還要負責把它啟動起來。
④被綁定的服務(wù)進程需要響應(yīng)綁定壳快,執(zhí)行具體的操作纸巷,并在成功完成后通知AMS,然后由AMS再回調(diào)發(fā)起請求的應(yīng)用程序(回調(diào)接口是ServiceConnection)
當Activity調(diào)用bindService時眶痰,最終調(diào)用到的是ContextImpl.bindService
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
...
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
...
}
那么何暇,應(yīng)用程序又是如何找到AMS并與之建立聯(lián)系的呢?
和ServiceManager一樣凛驮,AMS也同樣提供了ActivityManagerNative和ActivityManagerProxy代理
//ActivityManagerNative.getDefault()
static public IActivityManager getDefault() {
return gDefault.get();
}
這個gDefault.get又是什么
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
...
IActivityManager am = asInterface(b);
...
return am;
}
};
可以看到這里是通過單例模式來創(chuàng)建一個IActivityManager對象。
之后通過ServiceManager.getService("activity")方法取得ActivityManagerService的IBinder對象条辟。
接著調(diào)用asInterface黔夭,通過這個IBinder創(chuàng)建一個可用的ActivityManagerProxy
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
此外,ActivityManagerNative的另一個作用是為ActivityManagerService的實現(xiàn)提供便利羽嫡。AMS只需要繼承自ActivityManagerNative就可以將用戶的業(yè)務(wù)請求碼與自己的內(nèi)部實現(xiàn)函數(shù)連接起來本姥。
四.AIDL——Android接口描述語言
它是用于定義客戶端/服務(wù)端通信接口的一種描述語言。
要知道Server Manager本身也是一個BinderServer,AMS,WMS也是一個BinderServer杭棵,那么從Service Manager的實現(xiàn)來看婚惫,構(gòu)建一個Binder Server所需的工作為:
1.啟動的時機
比如SM是開機的時候通過init.rc文件啟動的氛赐,這就保證了它是系統(tǒng)中第一個注冊成服務(wù)大管家的Server,而WMS,AMS這些則是在SystemServer中啟動的先舷。
2.提供一致的服務(wù)接口
一個Binder Server應(yīng)該向公眾暴露它所能提供的服務(wù)艰管,而且客戶端使用的服務(wù)接口和服務(wù)器實現(xiàn)的服務(wù)接口必須是完全一致的。
對于WMS
通過分析aidl文件以及由它轉(zhuǎn)換生成的java接口文件蒋川,我們知道一個AIDL接口包括了IwindowManager,IWindowManager.Stub和IWindowManagerManager.Stub.Proxy三個重要類牲芋。
后兩者分別面向與WMS的服務(wù)端和Binder Client本地代理的實現(xiàn),且都繼承與IWindowManager捺球,因而就保證了Client和Server是在完全一致的服務(wù)接口上進行通訊缸浦。
3.與Binder驅(qū)動的交互
一個Binder Server需要與Binder驅(qū)動做哪些交互呢?除去一系列必要的初始化以外(open,mmap等)氮兵,就是要通過不斷地ioctl來循環(huán)讀寫數(shù)據(jù)裂逐。
比如SM就是通過binder_loop函數(shù)中的一個for死循環(huán)來完成這一工作的。
而對于AMS,PMS這些泣栈,他們在SystemServer啟動的過程中會開啟一個線程來循環(huán)讀取消息卜高,這點和binder_loop的主體框架基本一致。
4.外界如何才能訪問到這個Server服務(wù)秩霍?
- ①Service在ServiceManager中注冊
這種方法普遍存在于Android系統(tǒng)服務(wù)中篙悯,如ActivityManagerService,WindowManagerService等都在ServiceManager中做了注冊铃绒。調(diào)用者只需要通過ServiceManager.getService(name)就可以獲取到Binder Server的本地代理鸽照,然后與之通信。
WMS可以通過addService把自己注冊到Service Manager中颠悬。因而任何Binder Client都可以通過SM的getService接口獲取到它的一個引用矮燎。稱為實名的Server。
- ②通過其他Server作為中介
也就是Binder Server不需要在ServiceManager中注冊赔癌,只需要通過一個第三方的實名Server诞外。
比如WindowSession。
匿名Server安全性高灾票。
更簡單的來構(gòu)建Binder Server就是使用AIDL.