本文首發(fā)于微信公眾號(hào)「后廠村碼農(nóng)」
關(guān)聯(lián)系列
Android AOSP基礎(chǔ)系列
Android系統(tǒng)啟動(dòng)系列
應(yīng)用進(jìn)程啟動(dòng)系列
Android深入四大組件系列
Android深入理解Context系列
Android深入理解JNI系列
Android解析WindowManager
Android解析WMS系列
Android解析AMS系列
Android包管理機(jī)制系列
Android輸入系統(tǒng)系列
前言
Binder原理是掌握系統(tǒng)底層原理的基石榜苫,也是進(jìn)階高級(jí)工程師的必備知識(shí)點(diǎn)训措,這篇文章不會(huì)過(guò)多介紹Binder原理蕊蝗,而是講解學(xué)習(xí)Binder前需要的掌握的知識(shí)點(diǎn)。
1.Linux和Android的IPC機(jī)制種類
IPC全名為inter-Process Communication钠龙,含義為進(jìn)程間通信,是指兩個(gè)進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過(guò)程。在Android和Linux中都有各自的IPC機(jī)制伤极,這里分別來(lái)介紹下蛹找。
1.1 Linux中的IPC機(jī)制種類
Linux中提供了很多進(jìn)程間通信機(jī)制,主要有管道(pipe)哨坪、信號(hào)(sinal)庸疾、信號(hào)量(semophore)、消息隊(duì)列(Message)当编、共享內(nèi)存(Share Memory)届慈、套接字(Socket)等。
管道
管道是Linux由Unix那里繼承過(guò)來(lái)的進(jìn)程間的通信機(jī)制忿偷,它是Unix早期的一個(gè)重要通信機(jī)制金顿。管道的主要思想是,在內(nèi)存中創(chuàng)建一個(gè)共享文件鲤桥,從而使通信雙方利用這個(gè)共享文件來(lái)傳遞信息揍拆。這個(gè)共享文件比較特殊,它不屬于文件系統(tǒng)并且只存在于內(nèi)存中茶凳。另外還有一點(diǎn)嫂拴,管道采用的是半雙工通信方式的,數(shù)據(jù)只能在一個(gè)方向上流動(dòng)贮喧。
簡(jiǎn)單的模型如下所示筒狠。
信號(hào)
信號(hào)是軟件層次上對(duì)中斷機(jī)制的一種模擬,是一種異步通信方式箱沦,進(jìn)程不必通過(guò)任何操作來(lái)等待信號(hào)的到達(dá)窟蓝。信號(hào)可以在用戶空間進(jìn)程和內(nèi)核之間直接交互,內(nèi)核可以利用信號(hào)來(lái)通知用戶空間的進(jìn)程發(fā)生了哪些系統(tǒng)事件饱普。信號(hào)不適用于信息交換运挫,比較適用于進(jìn)程中斷控制。
信號(hào)量
信號(hào)量是一個(gè)計(jì)數(shù)器套耕,用來(lái)控制多個(gè)進(jìn)程對(duì)共享資源的訪問(wèn)谁帕。它常作為一種鎖機(jī)制,防止某進(jìn)程正在訪問(wèn)共享資源時(shí)冯袍,其他進(jìn)程也訪問(wèn)該資源匈挖。主要作為進(jìn)程間以及同一進(jìn)程內(nèi)不同線程之間的同步手段。
消息隊(duì)列
消息隊(duì)列是消息的鏈表康愤,具有特定的格式儡循,存放在內(nèi)存中并由消息隊(duì)列標(biāo)識(shí)符標(biāo)識(shí),并且允許一個(gè)或多個(gè)進(jìn)程向它寫(xiě)入與讀取消息征冷。信息會(huì)復(fù)制兩次择膝,因此對(duì)于頻繁或者信息量大的通信不宜使用消息隊(duì)列。
共享內(nèi)存
多個(gè)進(jìn)程可以直接讀寫(xiě)的一塊內(nèi)存空間检激,是針對(duì)其他通信機(jī)制運(yùn)行效率較低而設(shè)計(jì)的肴捉。
為了在多個(gè)進(jìn)程間交換信息腹侣,內(nèi)核專門(mén)留出了一塊內(nèi)存區(qū),可以由需要訪問(wèn)的進(jìn)程將其映射到自己的私有地址空間齿穗。進(jìn)程就可以直接讀寫(xiě)這一塊內(nèi)存而不需要進(jìn)行數(shù)據(jù)的拷貝傲隶,從而大大的提高效率。
套接字
套接字是更為基礎(chǔ)的進(jìn)程間通信機(jī)制窃页,與其他方式不同的是跺株,套接字可用于不同機(jī)器之間的進(jìn)程間通信。
1.2 Android中的IPC機(jī)制
Android系統(tǒng)是基于Linux內(nèi)核的脖卖,在Linux內(nèi)核基礎(chǔ)上帖鸦,又拓展出了一些IPC機(jī)制。Android系統(tǒng)除了支持套接字胚嘲,還支持序列化作儿、Messenger、AIDL馋劈、Bundle攻锰、文件共享、ContentProvider妓雾、Binder等娶吞。Binder會(huì)在后面介紹,先來(lái)了解前面的IPC機(jī)制械姻。
序列化
序列化指的是Serializable/Parcelable妒蛇,Serializable是Java提供的一個(gè)序列化接口,是一個(gè)空接口楷拳,為對(duì)象提供標(biāo)準(zhǔn)的序列化和反序列化操作绣夺。Parcelable接口是Android中的序列化方式,更適合在Android平臺(tái)上使用欢揖,用起來(lái)比較麻煩陶耍,效率很高。
Messenger
Messenger在Android應(yīng)用開(kāi)發(fā)中的使用頻率不高她混,可以在不同進(jìn)程中傳遞Message對(duì)象烈钞,在Message中加入我們想要傳的數(shù)據(jù)就可以在進(jìn)程間的進(jìn)行數(shù)據(jù)傳遞了。Messenger是一種輕量級(jí)的IPC方案并對(duì)AIDL進(jìn)行了封裝坤按。
AIDL
AIDL全名為Android interface definition Language毯欣,即Android接口定義語(yǔ)言。Messenger是以串行的方式來(lái)處理客戶端發(fā)來(lái)的信息臭脓,如果有大量的消息發(fā)到服務(wù)端酗钞,服務(wù)端仍然一個(gè)一個(gè)的處理再響應(yīng)客戶端顯然是不合適的。另外還有一點(diǎn),Messenger用來(lái)進(jìn)程間進(jìn)行數(shù)據(jù)傳遞但是卻不能滿足跨進(jìn)程的方法調(diào)用算吩,這個(gè)時(shí)候就需要使用AIDL了。
Bundle
Bundle實(shí)現(xiàn)了Parcelable接口佃扼,所以它可以方便的在不同的進(jìn)程間傳輸偎巢。Acitivity、Service兼耀、Receiver都是在Intent中通過(guò)Bundle來(lái)進(jìn)行數(shù)據(jù)傳遞压昼。
文件共享
兩個(gè)進(jìn)程通過(guò)讀寫(xiě)同一個(gè)文件來(lái)進(jìn)行數(shù)據(jù)共享,共享的文件可以是文本瘤运、XML窍霞、JOSN。文件共享適用于對(duì)數(shù)據(jù)同步要求不高的進(jìn)程間通信拯坟。
ContentProvider
ContentProvider為存儲(chǔ)和獲取數(shù)據(jù)了提供統(tǒng)一的接口但金,它可以在不同的應(yīng)用程序之間共享數(shù)據(jù),本身就是適合進(jìn)程間通信的郁季。ContentProvider底層實(shí)現(xiàn)也是Binder冷溃,但是使用起來(lái)比AIDL要容易許多。系統(tǒng)中很多操作都采用了ContentProvider梦裂,例如通訊錄似枕,音視頻等,這些操作本身就是跨進(jìn)程進(jìn)行通信年柠。
2.Linux和Binder的IPC通信原理
在講到Linux的進(jìn)程通信原理之前凿歼,我們需要先了解Liunx中的幾個(gè)概念。
內(nèi)核空間和用戶空間
當(dāng)我們接觸到Liunx時(shí)冗恨,免不了聽(tīng)到兩個(gè)詞答憔,User space(用戶空間)和 Kernel space(內(nèi)核空間),那么它們的含義是什么呢掀抹?
為了保護(hù)用戶進(jìn)程不能直接操作內(nèi)核攀唯,保證內(nèi)核的安全,操作系統(tǒng)從邏輯上將虛擬空間劃分為用戶空間和內(nèi)核空間渴丸。Linux 操作系統(tǒng)將最高的1GB字節(jié)供內(nèi)核使用侯嘀,稱為內(nèi)核空間,較低的3GB 字節(jié)供各進(jìn)程使用谱轨,稱為用戶空間戒幔。
內(nèi)核空間是Linux內(nèi)核的運(yùn)行空間,用戶空間是用戶程序的運(yùn)行空間土童。為了安全诗茎,它們是隔離的,即使用戶的程序崩潰了,內(nèi)核也不會(huì)受到影響敢订。內(nèi)核空間的數(shù)據(jù)是可以進(jìn)程間共享的王污,而用戶空間則不可以。比如在上圖進(jìn)程A的用戶空間是不能和進(jìn)程B的用戶空間共享的楚午。
進(jìn)程隔離
進(jìn)程隔離指的是昭齐,一個(gè)進(jìn)程不能直接操作或者訪問(wèn)另一個(gè)進(jìn)程。也就是進(jìn)程A不可以直接訪問(wèn)進(jìn)程B的數(shù)據(jù)矾柜。
系統(tǒng)調(diào)用
用戶空間需要訪問(wèn)內(nèi)核空間阱驾,就需要借助系統(tǒng)調(diào)用來(lái)實(shí)現(xiàn)。系統(tǒng)調(diào)用是用戶空間訪問(wèn)內(nèi)核空間的唯一方式怪蔑,保證了所有的資源訪問(wèn)都是在內(nèi)核的控制下進(jìn)行的里覆,避免了用戶程序?qū)ο到y(tǒng)資源的越權(quán)訪問(wèn),提升了系統(tǒng)安全性和穩(wěn)定性缆瓣。
進(jìn)程A和進(jìn)程B的用戶空間可以通過(guò)如下系統(tǒng)函數(shù)和內(nèi)核空間進(jìn)行交互喧枷。
- copy_from_user:將用戶空間的數(shù)據(jù)拷貝到內(nèi)核空間。
- copy_to_user:將內(nèi)核空間的數(shù)據(jù)拷貝到用戶空間弓坞。
內(nèi)存映射
由于應(yīng)用程序不能直接操作設(shè)備硬件地址割去,所以操作系統(tǒng)提供了一種機(jī)制:內(nèi)存映射,把設(shè)備地址映射到進(jìn)程虛擬內(nèi)存區(qū)昼丑。
舉個(gè)例子呻逆,如果用戶空間需要讀取磁盤(pán)的文件,如果不采用內(nèi)存映射菩帝,那么就需要在內(nèi)核空間建立一個(gè)頁(yè)緩存咖城,頁(yè)緩存去拷貝磁盤(pán)上的文件,然后用戶空間拷貝頁(yè)緩存的文件呼奢,這就需要兩次拷貝宜雀。
采用內(nèi)存映射,如下圖所示握础。
由于新建了虛擬內(nèi)存區(qū)域辐董,那么磁盤(pán)文件和虛擬內(nèi)存區(qū)域就可以直接映射,少了一次拷貝禀综。
內(nèi)存映射全名為Memory Map简烘,在Linux中通過(guò)系統(tǒng)調(diào)用函數(shù)mmap來(lái)實(shí)現(xiàn)內(nèi)存映射。將用戶空間的一塊內(nèi)存區(qū)域映射到內(nèi)核空間定枷。映射關(guān)系建立后孤澎,用戶對(duì)這塊內(nèi)存區(qū)域的修改可以直接反應(yīng)到內(nèi)核空間调炬,反之亦然衍慎。內(nèi)存映射能減少數(shù)據(jù)拷貝次數(shù)错敢,實(shí)現(xiàn)用戶空間和內(nèi)核空間的高效互動(dòng)偶垮。
2.1 Linux的IPC通信原理
了解Liunx中的幾個(gè)概念后泼返,就可以學(xué)習(xí)Linux的IPC通信原理了鹉勒,如下圖所示够挂。
內(nèi)核程序在內(nèi)核空間分配內(nèi)存并開(kāi)辟一塊內(nèi)核緩存區(qū)顷啼,發(fā)送進(jìn)程通過(guò)copy_from_user函數(shù)將數(shù)據(jù)拷貝到到內(nèi)核空間的緩沖區(qū)中。同樣的七兜,接收進(jìn)程在接收數(shù)據(jù)時(shí)在自己的用戶空間開(kāi)辟一塊內(nèi)存緩存區(qū)丸凭,然后內(nèi)核程序調(diào)用 copy_to_user() 函數(shù)將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到接收進(jìn)程。這樣數(shù)據(jù)發(fā)送進(jìn)程和數(shù)據(jù)接收進(jìn)程完成了一次數(shù)據(jù)傳輸惊搏,也就是一次進(jìn)程間通信贮乳。
Linux的IPC通信原理有兩個(gè)問(wèn)題:
- 一次數(shù)據(jù)傳遞需要經(jīng)歷:用戶空間 --> 內(nèi)核緩存區(qū) --> 用戶空間亚茬,需要2次數(shù)據(jù)拷貝碗暗,這樣效率不高颂砸。
- 接收數(shù)據(jù)的緩存區(qū)由數(shù)據(jù)接收進(jìn)程提供,但是接收進(jìn)程并不知道需要多大的空間來(lái)存放將要傳遞過(guò)來(lái)的數(shù)據(jù),因此只能開(kāi)辟盡可能大的內(nèi)存空間或者先調(diào)用API接收消息頭來(lái)獲取消息體的大小,浪費(fèi)了空間或者時(shí)間切蟋。
2.2 Binder的通信原理
Binder是基于開(kāi)源的OpenBinder實(shí)現(xiàn)的匆绣,OpenBinder最早并不是由Google公司開(kāi)發(fā)的愕把,而是Be Inc公司開(kāi)發(fā)的嚣镜,接著由Palm, Inc.公司負(fù)責(zé)開(kāi)發(fā)计福。后來(lái)OpenBinder的作者Dianne Hackborn加入了Google公司跌捆,并負(fù)責(zé)Android平臺(tái)的開(kāi)發(fā)工作,順便把這項(xiàng)技術(shù)也帶進(jìn)了Android说订。
Binder是基于內(nèi)存映射來(lái)實(shí)現(xiàn)的抄瓦,在前面我們知道內(nèi)存映射通常是用在有物理介質(zhì)的文件系統(tǒng)上的闺鲸,Binder沒(méi)有物理介質(zhì),它使用內(nèi)存映射是為了跨進(jìn)程傳遞數(shù)據(jù)埃叭。
Binder通信的步驟如下所示摸恍。
1.Binder驅(qū)動(dòng)在內(nèi)核空間創(chuàng)建一個(gè)數(shù)據(jù)接收緩存區(qū)。
2.在內(nèi)核空間開(kāi)辟一塊內(nèi)核緩存區(qū)赤屋,建立內(nèi)核緩存區(qū)和數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系立镶,以及數(shù)據(jù)接收緩存區(qū)和接收進(jìn)程用戶空間地址的映射關(guān)系。
3.發(fā)送方進(jìn)程通過(guò)copy_from_user()函數(shù)將數(shù)據(jù)拷貝 到內(nèi)核中的內(nèi)核緩存區(qū)类早,由于內(nèi)核緩存區(qū)和接收進(jìn)程的用戶空間存在內(nèi)存映射媚媒,因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了接收進(jìn)程的用戶空間,這樣便完成了一次進(jìn)程間的通信涩僻。
整個(gè)過(guò)程只使用了1次拷貝缭召,不會(huì)因?yàn)椴恢罃?shù)據(jù)的大小而浪費(fèi)空間或者時(shí)間栈顷,效率更高。
3.為什么要使用Binder
Android是基于Linux內(nèi)核的 嵌巷,Linux提供了很多IPC機(jī)制萄凤,而Android卻自己設(shè)計(jì)了Binder來(lái)進(jìn)行通信,主要是因?yàn)橐韵聨c(diǎn)搪哪。
性能方面
性能方面主要影響的因素是拷貝次數(shù)靡努,管道、消息隊(duì)列晓折、Socket的拷貝次書(shū)都是兩次惑朦,性能不是很好,共享內(nèi)存不需要拷貝漓概,性能最好漾月,Binder的拷貝次書(shū)為1次,性能僅次于內(nèi)存拷貝垛耳。
穩(wěn)定性方面
Binder是基于C/S架構(gòu)的栅屏,這個(gè)架構(gòu)通常采用兩層結(jié)構(gòu)飘千,在技術(shù)上已經(jīng)很成熟了堂鲜,穩(wěn)定性是沒(méi)有問(wèn)題的。共享內(nèi)存沒(méi)有分層护奈,難以控制缔莲,并發(fā)同步訪問(wèn)臨界資源時(shí),可能還會(huì)產(chǎn)生死鎖霉旗。從穩(wěn)定性的角度講痴奏,Binder是優(yōu)于共享內(nèi)存的。
安全方面
Android是一個(gè)開(kāi)源的系統(tǒng)厌秒,并且擁有開(kāi)放性的平臺(tái)读拆,市場(chǎng)上應(yīng)用來(lái)源很廣,因此安全性對(duì)于Android 平臺(tái)而言極其重要鸵闪。
傳統(tǒng)的IPC接收方無(wú)法獲得對(duì)方可靠的進(jìn)程用戶ID/進(jìn)程ID(UID/PID)檐晕,無(wú)法鑒別對(duì)方身份。Android 為每個(gè)安裝好的APP分配了自己的UID蚌讼,通過(guò)進(jìn)程的UID來(lái)鑒別進(jìn)程身份辟灰。另外,Android系統(tǒng)中的Server端會(huì)判斷UID/PID是否滿足訪問(wèn)權(quán)限篡石,而對(duì)外只暴露Client端芥喇,加強(qiáng)了系統(tǒng)的安全性。
語(yǔ)言方面
Linux是基于C語(yǔ)言凰萨,C語(yǔ)言是面向過(guò)程的继控,Android應(yīng)用層和Java Framework是基于Java語(yǔ)言械馆,Java語(yǔ)言是面向?qū)ο蟮摹inder本身符合面向?qū)ο蟮乃枷胛渫ǎ虼俗鳛锳ndroid的通信機(jī)制更合適不過(guò)狱杰。
從這四方面來(lái)看,Linux提供的大部分IPC機(jī)制根本無(wú)法和Binder相比較厅须,而共享內(nèi)存只在性能方面優(yōu)于Binder仿畸,其他方面都劣于Binder,這些就是為什么Android要使用Binder來(lái)進(jìn)行進(jìn)程間通信朗和,當(dāng)然系統(tǒng)中并不是所有的進(jìn)程通信都是采用了Binder错沽,而是根據(jù)場(chǎng)景選擇最合適的,比如Zygote進(jìn)程與AMS通信使用的是Socket眶拉,Kill Process采用的是信號(hào)千埃。
4.為什么要學(xué)習(xí)Binder?
Binder機(jī)制在Android中的地位舉足輕重,我們需要掌握的很多原理都和Binder有關(guān):
- 系統(tǒng)中的各個(gè)進(jìn)程是如何通信的忆植?
- Android系統(tǒng)啟動(dòng)過(guò)程
- AMS放可、PMS的原理
- 四大組件的原理,比如Activity是如何啟動(dòng)的朝刊?
- 插件化原理
- 系統(tǒng)服務(wù)的Client端和Server端是如何通信的耀里?(比如MediaPlayer和MeidaPlayerService)
上面只是列了一小部分,簡(jiǎn)單來(lái)說(shuō)說(shuō)拾氓,比如系統(tǒng)在啟動(dòng)時(shí)冯挎,SystemServer進(jìn)程啟動(dòng)后會(huì)創(chuàng)建Binder線程池,目的是通過(guò)Binder咙鞍,使得在SystemServer進(jìn)程中的服務(wù)可以和其他進(jìn)程進(jìn)行通信了房官。再比如我們常說(shuō)的AMS、PMS都是基于Binder來(lái)實(shí)現(xiàn)的续滋,拿PMS來(lái)說(shuō)翰守,PMS運(yùn)行在SystemServer進(jìn)程,如果它想要和DefaultContainerService通信(是用于檢查和復(fù)制可移動(dòng)文件的系統(tǒng)服務(wù))疲酌,就需要通過(guò)Binder蜡峰,因?yàn)镈efaultContainerService運(yùn)行在com.android.defcontainer進(jìn)程。
還有一個(gè)比較常見(jiàn)的C/S架構(gòu)間通信的問(wèn)題徐勃,Client端的MediaPlayer和Server端的MeidaPlayerService不是運(yùn)行在一個(gè)進(jìn)程中的事示,同樣需要Binder來(lái)實(shí)現(xiàn)通信。
可以說(shuō)Binder機(jī)制是掌握系統(tǒng)底層原理的基石僻肖。根據(jù)Android系統(tǒng)的分層肖爵,Binder機(jī)制主要分為以下幾個(gè)部分。
上圖并沒(méi)有給出Binder機(jī)制的具體的細(xì)節(jié)臀脏,而是先給出了一個(gè)概念劝堪,根據(jù)系統(tǒng)的Android系統(tǒng)的分層冀自,我將Binder機(jī)制分為了Java Binder、Native Binder秒啦、Kernel Binder熬粗,實(shí)際上Binder的內(nèi)容非常多,完全可以寫(xiě)一本來(lái)介紹余境,但是對(duì)于應(yīng)用開(kāi)發(fā)來(lái)說(shuō)驻呐,并不需要掌握那么多的知識(shí)點(diǎn),因此本系列主要會(huì)講解Java Binder和Native Binder芳来。
感謝
https://mp.weixin.qq.com/s/NBm5lh8_ZLfodOXT8Ph5iA
https://www.zhihu.com/question/39440766/answer/89210950
https://blog.csdn.net/carson_ho/article/details/73560642
更多的內(nèi)容請(qǐng)關(guān)注我的獨(dú)立博客的知識(shí)體系:
http://liuwangshu.cn/system/
這里不僅分享大前端含末、Android、Java等技術(shù)即舌,還有程序員成長(zhǎng)類文章佣盒。