為了加深對(duì)android中binder進(jìn)程間通訊流程的記憶草冈,這里記錄一下自己對(duì)binder的理解思路勃黍。
首先Android中binder的進(jìn)程間通訊其實(shí)可以類比java中線程間的通訊器虾,只不過(guò)binder的進(jìn)程間通訊多了一些約束而已阱飘。具體流程看下面屏镊;
與線程間通信類比
假設(shè)現(xiàn)在有線程Thread1
和線程Thread2
需要通訊针史,Thread1
需要把一個(gè)數(shù)據(jù)data
傳遞給Thread2
那最簡(jiǎn)單的方法就是利用一個(gè)靜態(tài)變量做一下中轉(zhuǎn),這里就把這個(gè)靜態(tài)變量定為MiddleWare.temp
赋访,那具體流程就可以用下面這段偽代碼來(lái)表示可都;
MiddleWare.temp = thread1.data;
thread2.data = MiddleWare.temp;
那么再進(jìn)行如下類比
-
MiddleWare
類比Binder驅(qū)動(dòng)(Binder驅(qū)動(dòng)可以簡(jiǎn)單的理解成內(nèi)核中的一個(gè)應(yīng)用) -
Thread1
類比進(jìn)程1 -
Thread2
類比進(jìn)程2
那上面?zhèn)未a中的兩個(gè)流程就是Binder驅(qū)動(dòng)進(jìn)程間通信的核心邏輯了,是不是很簡(jiǎn)單蚓耽∏總結(jié)一下Binder的作用就是暫存一下數(shù)據(jù)并且交給另一方;
mmap的理解
Binder和其它進(jìn)程間通訊手段有一個(gè)不同就是Binder可以實(shí)現(xiàn)一次copy來(lái)完成數(shù)據(jù)通信步悠,這里繼續(xù)通過(guò)上面那個(gè)類比來(lái)說(shuō)明一下mmap的原理签杈;
普通跨進(jìn)程通信兩次拷貝方案
上面的例子中通過(guò)類比了解了Binder(也就是例子中的MiddleWare)的角色,說(shuō)白了其實(shí)就是一個(gè)中轉(zhuǎn)站鼎兽,而Thread1
和Thread2
則是真正通信的對(duì)象答姥。但是這里類比中有一個(gè)關(guān)鍵的地方線程和進(jìn)程是不同的;就是進(jìn)程之間的內(nèi)存是不共享的谚咬;
這里先補(bǔ)充2個(gè)背景知識(shí):
- Linux內(nèi)存分布:Linux內(nèi)存分為內(nèi)核區(qū)和用戶區(qū)的踢涌,我們一般的進(jìn)程(如app應(yīng)用)都是跑在用戶區(qū)的,而一些系統(tǒng)進(jìn)程(如Binder驅(qū)動(dòng))則是跑在內(nèi)核區(qū)的序宦;
- 內(nèi)核區(qū)和用戶區(qū)區(qū)別:這里只要明白內(nèi)核區(qū)是可以訪問(wèn)(通過(guò)Copy)用戶區(qū)的數(shù)據(jù)睁壁,用戶區(qū)是無(wú)法訪問(wèn)內(nèi)核區(qū)的數(shù)據(jù)就可以了;當(dāng)然用戶區(qū)的不同進(jìn)程自然不可以互相訪問(wèn)數(shù)據(jù)互捌,不然要搞出一個(gè)Binder有啥用潘明;
這里對(duì)內(nèi)核區(qū)用戶區(qū)做一個(gè)簡(jiǎn)單的總結(jié):Binder所在的內(nèi)存區(qū)域權(quán)限最高,Binder可以拿到進(jìn)程1的數(shù)據(jù)也可以拿到進(jìn)程2的數(shù)據(jù)秕噪,同理也可以把數(shù)據(jù)給進(jìn)程1和進(jìn)程2钳降,而進(jìn)程1和進(jìn)程2拿不到Binder所在內(nèi)存的數(shù)據(jù),進(jìn)程1和進(jìn)程2的數(shù)據(jù)是互相不共享的腌巾;
繼續(xù)上面用線程來(lái)類比遂填,那現(xiàn)在的情況就是這樣的:
-
MiddleWare
獨(dú)自在一個(gè)線程; -
Thread1
在自己的線程1澈蝙; -
Thread2
在自己的線程2吓坚; -
MiddleWare
需要通過(guò)數(shù)據(jù)Copy來(lái)獲取或賦予Thread1``Thread2
的數(shù)據(jù)
那具體流程就變成了下面這段偽代碼:
MiddleWare.temp.copyFrom(thread1.data);
MiddleWare.temp.copyTo(thread2.data);
這里就是一般的跨進(jìn)程通信的核心流程了,而從上面分析可以看出一般跨進(jìn)程通信需要對(duì)數(shù)據(jù)進(jìn)行兩次Copy
mmap數(shù)據(jù)的一次拷貝
Android為什么用Binder作為跨進(jìn)程通信的手段自然是因?yàn)锽inder的高性能了灯荧,Binder通過(guò)mmap來(lái)將兩次Copy優(yōu)化至一次Copy礁击;
mmap是什么?mmap的全稱為MemoryMap也就是內(nèi)存映射,簡(jiǎn)單的說(shuō)可以把內(nèi)核區(qū)也就是Binder所在的內(nèi)存空間中的內(nèi)存映射到用戶區(qū)哆窿,這樣用戶區(qū)拿這部分?jǐn)?shù)據(jù)就可以不用Copy了链烈。
mmap既然可以直接映射內(nèi)存把內(nèi)核區(qū)的映射到用戶區(qū)那假如按照如下方案:
如圖按照上圖的做法是不是只要兩次映射,都不需要Copy就完成數(shù)據(jù)的傳輸也就是跨進(jìn)程的通訊了挚躯?這里我一開(kāi)始也是這樣想的强衡,但事實(shí)不是這樣的;
這里有一點(diǎn)需要注意码荔,就是mmap內(nèi)存映射后漩勤,用戶區(qū)的數(shù)據(jù)要到內(nèi)核區(qū)還是需要通過(guò)Copy才能完成的,而用戶區(qū)獲取數(shù)據(jù)則可以直接通過(guò)mmap內(nèi)存映射讀取內(nèi)核區(qū)的數(shù)據(jù)目胡,不再需要再經(jīng)過(guò)Copy(好像還需要一次淺拷貝,替換一下內(nèi)核區(qū)的引用链快?)
那繼續(xù)就上面這個(gè)類比的例子目前經(jīng)過(guò)mmap優(yōu)化過(guò)以后的偽代碼就是這樣的:
//把Thread1的數(shù)據(jù) 從用戶區(qū)Copy到 Thread2在MiddleWare中
//的映射區(qū)域MiddleWare.thread2MapData中去
MiddleWare.thread2MapData.copyFrom(thread1.data);
//Thread2通過(guò)mmap內(nèi)存映射獲取對(duì)應(yīng)映射區(qū)
//域MiddleWare.thread2MapData的數(shù)據(jù)
thread2.data = MiddleWare.thread2MapData;
這里再附上一個(gè)圖幫助理解
(Thread MiddleWare的類比關(guān)系應(yīng)該不用在說(shuō)了吧)
這里就可以清楚的看到Binder中mmap是怎么通過(guò)一次Copy來(lái)完成數(shù)據(jù)的傳輸?shù)模?br> 至于Binder的C/S架構(gòu)誰(shuí)是Client誰(shuí)是Server誉己,在當(dāng)前的例子中自然是Thread1(進(jìn)程1)是Client,Thread2(進(jìn)程2)是服務(wù)端了域蜗。當(dāng)然這個(gè)角色都是會(huì)發(fā)生變化的巨双,比如Thread1發(fā)送數(shù)據(jù)后Thread2處理完數(shù)據(jù)要把結(jié)果返回給Thread1,那此時(shí)Thread2就是Client端霉祸,Thread1就是Server端了筑累。這里不變的依據(jù)就是誰(shuí)接受數(shù)據(jù)并且處理數(shù)據(jù)誰(shuí)就是Server端,誰(shuí)發(fā)送數(shù)據(jù)誰(shuí)就是Client端丝蹭;
上面就是我自己對(duì)Binder通信流程的一部分理解和總結(jié)了慢宗,通過(guò)線程間通訊來(lái)類比也是為了方便記憶。當(dāng)然這里還沒(méi)有完奔穿,這里還有幾個(gè)疑問(wèn):
- 進(jìn)程1是如何找到進(jìn)程2的镜沽,或者說(shuō)找到進(jìn)程2在內(nèi)核區(qū)映射的內(nèi)存地址?
- 相對(duì)于Native層Framework層Binder的架構(gòu)又是怎樣的贱田?
- Binder看起來(lái)很虛缅茉,那我們平時(shí)要怎么用Binder這個(gè)東西?(AIDL男摧?它的實(shí)質(zhì)是什么)
上面這些疑問(wèn)我后面會(huì)陸續(xù)整理的蔬墩,至于BpBinder、BBinder耗拓、BinderProxy拇颅、IBinder這些亂七八糟的也都是為了完成這個(gè)架構(gòu)而提出的,有時(shí)間會(huì)再整理說(shuō)明乔询。