Binder是什麼?
Binder可以實現進程與進程之間的通信(IPC), Binder是Android底層系統的一個特色了,它很好地解決了進程間通訊的問題。
可能很多小夥伴對Binder
感覺有點兒陌生,但是Binder
在Android系統中無處不在,比如:
- 媒體的播放
- 音視頻捕獲
- 傳感器使用
- startActivity()/startService()
- 等...
Binder是Android獨有的跨線程通訊機制,它的運行機制和現實中的一個例子很像,我們來看一張圖
這張圖很形象的提現了Binder的運行機制,有Client(個人電腦)
,Server(應用服務器)
,Binder(路由器)
,ServiceManager(DNS服務器)
Binder
對服務端(Server)而言相當於服務端提供特定服務的接入點
,想要對接該服務就要從這個接入點
入手 對於客戶端(Client)而言,Binder相當於通向服務端(Server)管道
的入口,要想和服務端(Server)某個服務通訊,必須先建立管道,並獲得管道的入口,也就是接入點
ServiceManager
相當於DNS服務器
注:
這裏只是舉個形象的栗子,具體是怎樣的,都做了什麼,下面會慢慢講~
Android爲什麼使用Binder做IPC?
爲什麼Android會採用Binder做IPC(進程間通訊)呢?這也是Binder
的由來,首先Linux
中是有多種跨進程通訊的方式,但是它們不太適用於Android的跨進程通訊的場景,我們大概來看下:
- 管道 大多是指半雙工管道,半雙工管道指的是,A給B數據和,B給A數據是兩件事情,只允許數據在一個方向上傳輸,類似於對講機,同一時間雙方只能有一方發送數據,而全雙工就像電話,可以雙方同時發送/接收數據,這種方式是非常消耗內存的(具體可百度~)
- 共享內存 共享內存值得是多個進程可以訪問同一塊內存空間,這種方式管理會很混亂~
- Socket Socket相對來說更適合的是網絡通訊,對於進程間通訊顯然不夠和諧~
所以Binder
是應需求而生,前面三種方式只是說了不是和Android的進程建通訊,那麼Binder爲什麼適合呢?
主要是兩個方面
安全性
Binder協議支持對通訊雙方的身份信息進行較校驗,既支持匿名
的Binder也支持實名
的Binder,像傳統的Socket通訊,並沒有嚴格的身份校驗,只要知道ip地址就可以訪問,在Android中每個應用安裝成功都會分配一個唯一的UID
,而每個進程都有一個PID
,例如在Android9.0源碼中startActivity()
會對UID
和PID
做校驗,下面會提到~性能
Binder機制在進程間通訊時,數據只需Copy一次
,而傳統的通訊方式,比如管道
的方式需要Copy
兩次,性能方面僅次於共享內存的方式~
另外還有一點是爲什麼Binder設計的是Client/Server
的形式,因爲系統提供了一個服務,可能很多app都需要使用該服務,所以是一個一對多
的場景,所以Binder採用的是Client/Server
的形式
如圖(管道方式需要兩次Copy操作):
Binder中四個重要的角色
- Client 客戶端
- Server 服務端
- ServiceManager 就像上文所述的DNS服務器,它的主要作用是
Client
和Server
之間的橋樑,Client
可以通過ServiceManager
拿到Server
中Binder
實體的引用 - Binder驅動是連接
Client
、Server
和ServiceManager
的橋樑,Android重很多系統服務是通過Binder
拿到的,比如context.getSystemService (Context.AUDIO_SERVICE)
獲取音量的服務
其中前三者Client
、Server
、ServiceManager
都屬於用戶空間,而Binder驅動
屬於內核空間 注意用戶空間是不可以進程間通訊的,內核空間是可以進程間通訊的 這裏需要主要的是Binder驅動
它是有個線程池的存在,有可能是併發, 這個線程池是由Binder驅動
管理的,一個進程的Binder線程數默認是16
,超過這個數會阻塞等待~
Binder中四個重要的對象
首先需要簡單說下AIDL
: AIDL是Android Interface definition language 安卓接口定義語言,是Binder
中Client進程
和Server進程
通訊的語言,是爲了Binder
簡化代碼的架構
- IBinder 是一個接口,表示可以實現跨進程通訊的能力,只需要實現找個接口就可以跨進程傳輸
- Iinterface 代表的Server進程具備什麼樣的功能、能力,能夠提供哪些方法,對應
AIDL
定義的接口 - Binder
Java層的
Binder類,代表的是Binder的本地對象,有個重要的內部類BinderProxy
- Stub 是使用AIDL時編譯工具自動生成一個
Stub
的靜態內部類,繼承自Binder
,是個抽象類,具體實現Iinterface
的接口的具體邏輯,開發者自己實現
Binder通訊流程
先看兩張圖,Binder通訊流程圖:
如圖: Binder通訊流程首先是,Client需要發送數據,做了(只做一次)copy from user
到BinderProxy
,BinderProxy
是可以操作內核的緩存區,內核的緩存區和Binder創建的內存映射(Binder創建的接收緩存區)是存在映射關係的,而服務端是與內存映射(Binder創建得接收緩存區)是存在直接的內存映射關係,所以只需要一次copy
操作,相當於這一次複製,直接將數據複製到了Server進程
的內存空間中去了。當然這中間室友校驗的,比如: descriptor
的Binder實體的引用
和Binder實體是否匹配
等
詳細流程圖:
源碼分析
下面從源碼角度簡單分析內核層主要做的以下步驟:
- 打開binder設備
- buffer創建 (用於進程間數據傳遞)
- 開闢內存映射 (128K)
- ServiceManager啓動
- 打包Parcel中,數據寫入binder設備,copy_from_user
- 服務註冊,添加到鏈表svclist中
- 定義主線程中的線程池
- 循環從mIn和mOut中取出讀寫請求,發到binder設備中
我們從Android源碼中都可以看到這些,下面代碼以Android9.0
爲例:
有興趣的小夥伴可以自行翻閱:Android在線源碼閱讀
首先我們看下ServiceManager啓動,ServiceManager
是在Android系統啓動時就就會喚起的服務可見system/core/rootdir/init.rc
的407
行:
start servicemanager
ServiceManager
會完成打開binder設備和開闢內存映射 (128K)的動作,可見device/google/cuttlefish_kernel/4.4-x86_64/System.map
的25306行
(該文件需要下載查看,不支持在線瀏覽):
ffffffff815dbf50 t binder_mmap
在frameworks/native/cmds/servicemanager/service_manager.c
的main()
方法中有:
if (argc > 1) {
driver = argv[1];
} else {
//打開Binder設備文件,返回文件描述符
driver = "/dev/binder";
}
//Binder的buffer創建,用於進程間數據傳輸,開啓128k大小的內存映射,路徑見下方
bs = binder_open(driver, 128*1024);
其實Service
的註冊也是在service_manager
中的do_add_service()
方法中完成的,這個不是Binder
的核心知識,簡單提下,感興趣的可以看下
//權限檢查
if (!svc_can_register(s, len, spid, uid)) {
ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
str8(s, len), handle, uid);
return -1;
}
//根據服務名在svclist鏈表上查找,看服務是否已經註冊
si = find_svc(s, len);
if (si) {
if (si->handle) {
//註冊過
ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
str8(s, len), handle, uid);
svcinfo_death(bs, si);
}
si->handle = handle;
} else {
//沒註冊,分配一個服務管理的結構svcinfo,並將其添加到鏈表的list當中
si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
if (!si) {
ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
str8(s, len), handle, uid);
return -1;
}
si->handle = handle;
si->len = len;
memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
si->name[len] = '\0';
si->death.func = (void*) svcinfo_death;
si->death.ptr = si;
si->allow_isolated = allow_isolated;
si->dumpsys_priority = dumpsys_priority;
//將代表該服務的結構插入到鏈表
si->next = svclist;
svclist = si;
}
//增加Binder的應用計數
binder_acquire(bs, handle);
//該服務退出需要通知ServiceManager
binder_link_to_death(bs, handle, &si->death);
return 0;
打開Binder設備驅動是在frameworks/native/cmds/servicemanager/binder.c
的binder_open()
方法中有這麼一行代碼:
//打開Binder設備驅動的時候,開啓128k大小的內存映射是在這裏執行的
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
打包Parcel,數據寫入binder設備,copy_from_user可見frameworks/native/libs/binder/IServiceManager.cpp
的addService()
方法: 這裏會將Service
相關信息打包成Parcel
對象,並且調用remote()->transact()
方法往下一步傳輸
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated, int dumpsysPriority) {
Parcel data, reply;
//Parcel對象打包過程
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
data.writeInt32(dumpsysPriority);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
數據寫入binder設備的過程在frameworks/native/libs/binder/IPCThreadState.cpp
中實現的writeTransactionData()
方法,
//這裏主要是將`Parcel`對象中的信息封裝成結構體,並且寫入到`mOut`當中
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
...
//將數據寫入Binder的設備當中,並等待返回結果
err = waitForResponse(reply);
另外從Binder設備中不停地讀寫的實現方式,是通過線程池
的方式(上文有提到),不停地去讀寫,具體可見: frameworks/native/libs/binder/IPCThreadState.cpp
的joinThreadPool()
方法,主要是定義了一個主線程中的線程池,
//將對象設爲當前線程的私有
pthread_setspecific(gTLS, this);
clearCaller();
//輸入buffer預分配256大小的空間
mIn.setDataCapacity(256);
//輸出buffer預分配256大小的空間
mOut.setDataCapacity(256);
對Binder設備數據的讀寫,主要的工作就是循環的對mIn
和mOut
進行IO
的讀寫,然後發送到Binder的設備中 對於數據是否需要讀取/寫的
// Is the read buffer empty?,是否有讀的請求
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We dont want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
// 是否有寫的請求
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
...
//將讀寫的請求數據發送到Binder設備中
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
分享
最後在這裏我分享自己收錄在整理的Android學習知識點文檔,裏面有Handler相關面試題和其他知識點的講解,希望可以幫助大家學習提升進階,也節省大家在網上搜索資料的時間來學習,可以分享給身邊好友一起學習
有需要的朋友可以點贊+評論+轉發,關注我,然後私信我【666】獲取,也可以點擊《Android學習PDF+架構視頻+面試文檔》查看更多;