本文首發(fā)于微信公眾號「后廠村碼農(nóng)」
關(guān)聯(lián)系列
Android AOSP基礎(chǔ)系列
Android系統(tǒng)啟動系列
應(yīng)用進(jìn)程啟動系列
Android深入四大組件系列
Android深入理解Context系列
Android深入理解JNI系列
Android解析WindowManager
Android解析WMS系列
Android解析AMS系列
Android包管理機(jī)制系列
Android輸入系統(tǒng)系列
前言
Binder原理是掌握系統(tǒng)底層原理的基石,也是進(jìn)階高級工程師的必備知識點,這篇文章不會過多介紹Binder原理,而是講解學(xué)習(xí)Binder前需要的掌握的知識點。
1.Linux和Android的IPC機(jī)制種類
IPC全名為inter-Process Communication,含義為進(jìn)程間通信,是指兩個進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過程。在Android和Linux中都有各自的IPC機(jī)制,這里分別來介紹下。
1.1 Linux中的IPC機(jī)制種類
Linux中提供了很多進(jìn)程間通信機(jī)制,主要有管道(pipe)、信號(sinal)、信號量(semophore)、消息隊列(Message)、共享內(nèi)存(Share Memory)、套接字(Socket)等。
管道
管道是Linux由Unix那里繼承過來的進(jìn)程間的通信機(jī)制,它是Unix早期的一個重要通信機(jī)制。管道的主要思想是,在內(nèi)存中創(chuàng)建一個共享文件,從而使通信雙方利用這個共享文件來傳遞信息。這個共享文件比較特殊,它不屬于文件系統(tǒng)并且只存在于內(nèi)存中。另外還有一點,管道采用的是半雙工通信方式的,數(shù)據(jù)只能在一個方向上流動。
簡單的模型如下所示。
信號
信號是軟件層次上對中斷機(jī)制的一種模擬,是一種異步通信方式,進(jìn)程不必通過任何操作來等待信號的到達(dá)。信號可以在用戶空間進(jìn)程和內(nèi)核之間直接交互,內(nèi)核可以利用信號來通知用戶空間的進(jìn)程發(fā)生了哪些系統(tǒng)事件。信號不適用于信息交換,比較適用于進(jìn)程中斷控制。
信號量
信號量是一個計數(shù)器,用來控制多個進(jìn)程對共享資源的訪問。它常作為一種鎖機(jī)制,防止某進(jìn)程正在訪問共享資源時,其他進(jìn)程也訪問該資源。主要作為進(jìn)程間以及同一進(jìn)程內(nèi)不同線程之間的同步手段。
消息隊列
消息隊列是消息的鏈表,具有特定的格式,存放在內(nèi)存中并由消息隊列標(biāo)識符標(biāo)識,并且允許一個或多個進(jìn)程向它寫入與讀取消息。信息會復(fù)制兩次,因此對于頻繁或者信息量大的通信不宜使用消息隊列。
共享內(nèi)存
多個進(jìn)程可以直接讀寫的一塊內(nèi)存空間,是針對其他通信機(jī)制運行效率較低而設(shè)計的。
為了在多個進(jìn)程間交換信息,內(nèi)核專門留出了一塊內(nèi)存區(qū),可以由需要訪問的進(jìn)程將其映射到自己的私有地址空間。進(jìn)程就可以直接讀寫這一塊內(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會在后面介紹,先來了解前面的IPC機(jī)制。
序列化
序列化指的是Serializable/Parcelable,Serializable是Java提供的一個序列化接口,是一個空接口,為對象提供標(biāo)準(zhǔn)的序列化和反序列化操作。Parcelable接口是Android中的序列化方式,更適合在Android平臺上使用,用起來比較麻煩,效率很高。
Messenger
Messenger在Android應(yīng)用開發(fā)中的使用頻率不高,可以在不同進(jìn)程中傳遞Message對象,在Message中加入我們想要傳的數(shù)據(jù)就可以在進(jìn)程間的進(jìn)行數(shù)據(jù)傳遞了。Messenger是一種輕量級的IPC方案并對AIDL進(jìn)行了封裝。
AIDL
AIDL全名為Android interface definition Language,即Android接口定義語言。Messenger是以串行的方式來處理客戶端發(fā)來的信息,如果有大量的消息發(fā)到服務(wù)端,服務(wù)端仍然一個一個的處理再響應(yīng)客戶端顯然是不合適的。另外還有一點,Messenger用來進(jìn)程間進(jìn)行數(shù)據(jù)傳遞但是卻不能滿足跨進(jìn)程的方法調(diào)用,這個時候就需要使用AIDL了。
Bundle
Bundle實現(xiàn)了Parcelable接口,所以它可以方便的在不同的進(jìn)程間傳輸。Acitivity、Service、Receiver都是在Intent中通過Bundle來進(jìn)行數(shù)據(jù)傳遞。
文件共享
兩個進(jìn)程通過讀寫同一個文件來進(jìn)行數(shù)據(jù)共享,共享的文件可以是文本、XML、JOSN。文件共享適用于對數(shù)據(jù)同步要求不高的進(jìn)程間通信。
ContentProvider
ContentProvider為存儲和獲取數(shù)據(jù)了提供統(tǒng)一的接口,它可以在不同的應(yīng)用程序之間共享數(shù)據(jù),本身就是適合進(jìn)程間通信的。ContentProvider底層實現(xiàn)也是Binder,但是使用起來比AIDL要容易許多。系統(tǒng)中很多操作都采用了ContentProvider,例如通訊錄,音視頻等,這些操作本身就是跨進(jìn)程進(jìn)行通信。
2.Linux和Binder的IPC通信原理
在講到Linux的進(jìn)程通信原理之前,我們需要先了解Liunx中的幾個概念。
內(nèi)核空間和用戶空間
當(dāng)我們接觸到Liunx時,免不了聽到兩個詞,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)核的運行空間,用戶空間是用戶程序的運行空間。為了安全,它們是隔離的,即使用戶的程序崩潰了,內(nèi)核也不會受到影響。內(nèi)核空間的數(shù)據(jù)是可以進(jìn)程間共享的,而用戶空間則不可以。比如在上圖進(jìn)程A的用戶空間是不能和進(jìn)程B的用戶空間共享的。
進(jìn)程隔離
進(jìn)程隔離指的是,一個進(jìn)程不能直接操作或者訪問另一個進(jìn)程。也就是進(jìn)程A不可以直接訪問進(jìn)程B的數(shù)據(jù)。
系統(tǒng)調(diào)用
用戶空間需要訪問內(nèi)核空間,就需要借助系統(tǒng)調(diào)用來實現(xiàn)。系統(tǒng)調(diào)用是用戶空間訪問內(nèi)核空間的唯一方式,保證了所有的資源訪問都是在內(nèi)核的控制下進(jìn)行的,避免了用戶程序?qū)ο到y(tǒng)資源的越權(quán)訪問,提升了系統(tǒng)安全性和穩(wěn)定性。
進(jìn)程A和進(jìn)程B的用戶空間可以通過如下系統(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ū)。
舉個例子,如果用戶空間需要讀取磁盤的文件,如果不采用內(nèi)存映射,那么就需要在內(nèi)核空間建立一個頁緩存,頁緩存去拷貝磁盤上的文件,然后用戶空間拷貝頁緩存的文件,這就需要兩次拷貝。
采用內(nèi)存映射,如下圖所示。
由于新建了虛擬內(nèi)存區(qū)域,那么磁盤文件和虛擬內(nèi)存區(qū)域就可以直接映射,少了一次拷貝。
內(nèi)存映射全名為Memory Map,在Linux中通過系統(tǒng)調(diào)用函數(shù)mmap來實現(xiàn)內(nèi)存映射。將用戶空間的一塊內(nèi)存區(qū)域映射到內(nèi)核空間。映射關(guān)系建立后,用戶對這塊內(nèi)存區(qū)域的修改可以直接反應(yīng)到內(nèi)核空間,反之亦然。內(nèi)存映射能減少數(shù)據(jù)拷貝次數(shù),實現(xiàn)用戶空間和內(nèi)核空間的高效互動。
2.1 Linux的IPC通信原理
了解Liunx中的幾個概念后,就可以學(xué)習(xí)Linux的IPC通信原理了,如下圖所示。
內(nèi)核程序在內(nèi)核空間分配內(nèi)存并開辟一塊內(nèi)核緩存區(qū),發(fā)送進(jìn)程通過copy_from_user函數(shù)將數(shù)據(jù)拷貝到到內(nèi)核空間的緩沖區(qū)中。同樣的,接收進(jìn)程在接收數(shù)據(jù)時在自己的用戶空間開辟一塊內(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通信原理有兩個問題:
- 一次數(shù)據(jù)傳遞需要經(jīng)歷:用戶空間 --> 內(nèi)核緩存區(qū) --> 用戶空間,需要2次數(shù)據(jù)拷貝,這樣效率不高。
- 接收數(shù)據(jù)的緩存區(qū)由數(shù)據(jù)接收進(jìn)程提供,但是接收進(jìn)程并不知道需要多大的空間來存放將要傳遞過來的數(shù)據(jù),因此只能開辟盡可能大的內(nèi)存空間或者先調(diào)用API接收消息頭來獲取消息體的大小,浪費了空間或者時間。
2.2 Binder的通信原理
Binder是基于開源的OpenBinder實現(xiàn)的,OpenBinder最早并不是由Google公司開發(fā)的,而是Be Inc公司開發(fā)的,接著由Palm, Inc.公司負(fù)責(zé)開發(fā)。后來OpenBinder的作者Dianne Hackborn加入了Google公司,并負(fù)責(zé)Android平臺的開發(fā)工作,順便把這項技術(shù)也帶進(jìn)了Android。
Binder是基于內(nèi)存映射來實現(xiàn)的,在前面我們知道內(nèi)存映射通常是用在有物理介質(zhì)的文件系統(tǒng)上的,Binder沒有物理介質(zhì),它使用內(nèi)存映射是為了跨進(jìn)程傳遞數(shù)據(jù)。
Binder通信的步驟如下所示。
1.Binder驅(qū)動在內(nèi)核空間創(chuàng)建一個數(shù)據(jù)接收緩存區(qū)。
2.在內(nèi)核空間開辟一塊內(nèi)核緩存區(qū),建立內(nèi)核緩存區(qū)和數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系,以及數(shù)據(jù)接收緩存區(qū)和接收進(jìn)程用戶空間地址的映射關(guān)系。
3.發(fā)送方進(jìn)程通過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)程間的通信。
整個過程只使用了1次拷貝,不會因為不知道數(shù)據(jù)的大小而浪費空間或者時間,效率更高。
3.為什么要使用Binder
Android是基于Linux內(nèi)核的 ,Linux提供了很多IPC機(jī)制,而Android卻自己設(shè)計了Binder來進(jìn)行通信,主要是因為以下幾點。
性能方面
性能方面主要影響的因素是拷貝次數(shù),管道、消息隊列、Socket的拷貝次書都是兩次,性能不是很好,共享內(nèi)存不需要拷貝,性能最好,Binder的拷貝次書為1次,性能僅次于內(nèi)存拷貝。
穩(wěn)定性方面
Binder是基于C/S架構(gòu)的,這個架構(gòu)通常采用兩層結(jié)構(gòu),在技術(shù)上已經(jīng)很成熟了,穩(wěn)定性是沒有問題的。共享內(nèi)存沒有分層,難以控制,并發(fā)同步訪問臨界資源時,可能還會產(chǎn)生死鎖。從穩(wěn)定性的角度講,Binder是優(yōu)于共享內(nèi)存的。
安全方面
Android是一個開源的系統(tǒng),并且擁有開放性的平臺,市場上應(yīng)用來源很廣,因此安全性對于Android 平臺而言極其重要。
傳統(tǒng)的IPC接收方無法獲得對方可靠的進(jìn)程用戶ID/進(jìn)程ID(UID/PID),無法鑒別對方身份。Android 為每個安裝好的APP分配了自己的UID,通過進(jìn)程的UID來鑒別進(jìn)程身份。另外,Android系統(tǒng)中的Server端會判斷UID/PID是否滿足訪問權(quán)限,而對外只暴露Client端,加強(qiáng)了系統(tǒng)的安全性。
語言方面
Linux是基于C語言,C語言是面向過程的,Android應(yīng)用層和Java Framework是基于Java語言,Java語言是面向?qū)ο蟮摹inder本身符合面向?qū)ο蟮乃枷?,因此作為Android的通信機(jī)制更合適不過。
從這四方面來看,Linux提供的大部分IPC機(jī)制根本無法和Binder相比較,而共享內(nèi)存只在性能方面優(yōu)于Binder,其他方面都劣于Binder,這些就是為什么Android要使用Binder來進(jìn)行進(jìn)程間通信,當(dāng)然系統(tǒng)中并不是所有的進(jìn)程通信都是采用了Binder,而是根據(jù)場景選擇最合適的,比如Zygote進(jìn)程與AMS通信使用的是Socket,Kill Process采用的是信號。
4.為什么要學(xué)習(xí)Binder?
Binder機(jī)制在Android中的地位舉足輕重,我們需要掌握的很多原理都和Binder有關(guān):
- 系統(tǒng)中的各個進(jìn)程是如何通信的?
- Android系統(tǒng)啟動過程
- AMS、PMS的原理
- 四大組件的原理,比如Activity是如何啟動的?
- 插件化原理
- 系統(tǒng)服務(wù)的Client端和Server端是如何通信的?(比如MediaPlayer和MeidaPlayerService)
上面只是列了一小部分,簡單來說說,比如系統(tǒng)在啟動時,SystemServer進(jìn)程啟動后會創(chuàng)建Binder線程池,目的是通過Binder,使得在SystemServer進(jìn)程中的服務(wù)可以和其他進(jìn)程進(jìn)行通信了。再比如我們常說的AMS、PMS都是基于Binder來實現(xiàn)的,拿PMS來說,PMS運行在SystemServer進(jìn)程,如果它想要和DefaultContainerService通信(是用于檢查和復(fù)制可移動文件的系統(tǒng)服務(wù)),就需要通過Binder,因為DefaultContainerService運行在com.android.defcontainer進(jìn)程。
還有一個比較常見的C/S架構(gòu)間通信的問題,Client端的MediaPlayer和Server端的MeidaPlayerService不是運行在一個進(jìn)程中的,同樣需要Binder來實現(xiàn)通信。
可以說Binder機(jī)制是掌握系統(tǒng)底層原理的基石。根據(jù)Android系統(tǒng)的分層,Binder機(jī)制主要分為以下幾個部分。
上圖并沒有給出Binder機(jī)制的具體的細(xì)節(jié),而是先給出了一個概念,根據(jù)系統(tǒng)的Android系統(tǒng)的分層,我將Binder機(jī)制分為了Java Binder、Native Binder、Kernel Binder,實際上Binder的內(nèi)容非常多,完全可以寫一本來介紹,但是對于應(yīng)用開發(fā)來說,并不需要掌握那么多的知識點,因此本系列主要會講解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)容請關(guān)注我的獨立博客的知識體系:
http://liuwangshu.cn/system/
這里不僅分享大前端、Android、Java等技術(shù),還有程序員成長類文章。