本文主要內(nèi)容
- Binder驅(qū)動接口
- ServiceManager的啟動
- Binder設(shè)計理念
- 總結(jié)
Binder驅(qū)動接口
Binder驅(qū)動一共有3個重要的接口:
- binder_open,打開binder驅(qū)動
- binder_mmap,內(nèi)存映射
- binder_ioctl,與binder驅(qū)動通信
binder_mmap是整套機制的基礎(chǔ),通過內(nèi)存映射,不同進(jìn)程間有一塊共享內(nèi)存,得以實現(xiàn)IPC。

binder_ioctl,類似于文件讀寫方法,它承擔(dān)了Binder驅(qū)動的大部分業(yè)務(wù),與Binder驅(qū)動通信。
看上圖,只需要把進(jìn)程A的內(nèi)存空間復(fù)制到內(nèi)核空間中(copy from user),內(nèi)核空間中有了進(jìn)程A的東西,然后通過內(nèi)存映射給進(jìn)程B讀取,所以說Binder通信只需要一次內(nèi)存復(fù)制
關(guān)于以上3個方法,后續(xù)再補充詳細(xì)說明。
ServiceManager的啟動
ServiceManager是非常重要的系統(tǒng)服務(wù),它類似于網(wǎng)絡(luò)世界中的DNS(請參見進(jìn)程能通信——智能指針)。
它是在init.rc中啟動的,代碼位于/framework/native/cmds/servicemanager 目錄中,sm啟動代碼如下:
//service_manager.c
int main(int argc, char** argv)
{
struct binder_state *bs;
char *driver;
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder";
}
//打開驅(qū)動
bs = binder_open(driver, 128*1024);
//將自己設(shè)置成context manager角色,ServiceManager系統(tǒng)中肯定只有一個
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
//進(jìn)入循環(huán)
binder_loop(bs, svcmgr_handler);
}
代碼較類似于socket通信server端代碼,新建ServerSocket,啟動死循環(huán),等待連接。查看binder_open代碼:
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
struct binder_state *bs;
bs = malloc(sizeof(*bs));
//真正地打開binder驅(qū)動
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
//內(nèi)存映射
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
return bs;
}
上一章中提到的binder驅(qū)動三大方法中,現(xiàn)在已經(jīng)出現(xiàn)倆了,binder_open方法中打開binder驅(qū)動,并且完成內(nèi)存映射。
接下來看看binder_loop方法:
void binder_loop(struct binder_state *bs, binder_handler func)
{
for (;;) {
//調(diào)用ioctl,與binder驅(qū)動通信,從驅(qū)動中讀取數(shù)據(jù),并存放在bwr指針中
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
//處理讀取的指令
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
}
}
binder_loop中存在一個死循環(huán),循環(huán)中不斷從驅(qū)動中讀取指令,并處理指令。接著看binder_parse方法:
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
while (ptr < end) {
switch(cmd) {
case BR_TRANSACTION:
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
if (func) {
//func是一個函數(shù)指針,在此調(diào)用函數(shù)并返回,此函數(shù)類似于onTransact
res = func(bs, txn, &msg, &reply);
if (txn->flags & TF_ONE_WAY) {
binder_free_buffer(bs, txn->data.ptr.buffer);
} else {
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
}
}
ptr += sizeof(*txn);
break;
}
}
}
binder_parse方法中,根據(jù)cmd的值執(zhí)行對應(yīng)的方法,當(dāng)客戶端調(diào)用transact方法時,ServiceManager會回調(diào)func函數(shù)。func是一個函數(shù)指針,它實質(zhì)上是:
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//調(diào)用do_find_service,查找對應(yīng)的service binder
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
case SVC_MGR_ADD_SERVICE:
//添加service
do_add_service(bs, s, len, handle, txn->sender_euid,allow_isolated, txn->sender_pid)
case SVC_MGR_LIST_SERVICES: {
//鏈表數(shù)據(jù)結(jié)構(gòu),返回節(jié)點
si = svclist;
while ((n-- > 0) && si)
si = si->next;
}
return 0;
}
所以,當(dāng)service manager從binder驅(qū)動中讀取指令,并根據(jù)不同的指令執(zhí)行不同的方法,最后將結(jié)果返回給binder驅(qū)動。
service manager與binder交互的邏輯,非常類似于socket通信,先打開binder驅(qū)動,再調(diào)用mmap方法映射內(nèi)存,最后啟動死循環(huán),調(diào)用ioctl方法從binder驅(qū)動中獲取指令,根據(jù)指令調(diào)用對應(yīng)方法,返回結(jié)果給binder驅(qū)動。
Binder設(shè)計理念
如果是我們自己來設(shè)計binder,那么我們該怎么做?
很明顯,參照service manager,在server端,用戶需要打開binder驅(qū)動,執(zhí)行內(nèi)存映射,建議死循環(huán),與binder驅(qū)動通信等等。client端應(yīng)用還是需要執(zhí)行以上步驟。過程相當(dāng)繁鎖,必須要進(jìn)行相應(yīng)封裝,以使用用戶開發(fā)。
而且以上代碼均是c或c++代碼,android上層用戶更多地使用java,需要給java用戶提供更友好的使用方式。
binder主要在兩個方面進(jìn)行封裝:
- 封裝與binder驅(qū)動交互的部分
- 使用代理,proxy
1、ProcessState和IPCThreadState
ProcessState,專門管理每個應(yīng)用進(jìn)程中的Binder操作,包括與打開驅(qū)動、內(nèi)存映射等等,每個進(jìn)程中只需要打開一次驅(qū)動,執(zhí)行一次內(nèi)存映射即可。
應(yīng)用進(jìn)程中肯定會有多個線程,每個線程都可能需要進(jìn)行進(jìn)程間通信,負(fù)責(zé)這個工作的就是IPCThreadState。實際上ProcessState只是負(fù)責(zé)打開驅(qū)動、內(nèi)存映射等,而與binder驅(qū)動實質(zhì)進(jìn)行通信的是IPCThreadState。
2、proxy
binder中proxy無處不在,用戶不便直接與service manager打交道,尤其是在java端,所以設(shè)計出proxy,從而更方便客戶調(diào)用。
總結(jié)
關(guān)于binder機制,其實以前也曾寫過一篇文章,Android binder機制(native服務(wù)篇),關(guān)于客戶端部分如何與server端交互,請參照上文,今天再次閱讀《深入理解android內(nèi)核設(shè)計思想》,有了新的感悟,果然是常看常新。
有時往往代碼特別多,不要害怕,宏大的代碼,無數(shù)的文件往往只是作者在掩蓋一些具體細(xì)節(jié),在封裝,方便用戶調(diào)用而已,而繁瑣的背后只是那一點點特別簡單的道理,比如binder機制,核心原理和socket通信幾乎沒有差別。只要我們抓住主線,不要怕麻煩,一切都能盡在掌握當(dāng)中
請允許我裝逼一下,一切有為法,皆夢幻泡影,如霧亦如電,應(yīng)作如是觀。