Android Binder原理(一)學習Binder前必須要了解的知識點

本文首發於微信公衆號「後廠村碼農」

關聯繫列
Android AOSP基礎系列
Android系統啓動系列
應用進程啓動系列
Android深入四大組件系列
Android深入理解Context系列
Android深入理解JNI系列
Android解析WindowManager
Android解析WMS系列
Android解析AMS系列
Android包管理機制系列
Android輸入系統系列

前言

Binder原理是掌握系統底層原理的基石,也是進階高級工程師的必備知識點,這篇文章不會過多介紹Binder原理,而是講解學習Binder前需要的掌握的知識點。

1.Linux和Android的IPC機制種類

IPC全名爲inter-Process Communication,含義爲進程間通信,是指兩個進程之間進行數據交換的過程。在Android和Linux中都有各自的IPC機制,這裏分別來介紹下。

1.1 Linux中的IPC機制種類

Linux中提供了很多進程間通信機制,主要有管道(pipe)、信號(sinal)、信號量(semophore)、消息隊列(Message)、共享內存(Share Memory)、套接字(Socket)等。

管道
管道是Linux由Unix那裏繼承過來的進程間的通信機制,它是Unix早期的一個重要通信機制。管道的主要思想是,在內存中創建一個共享文件,從而使通信雙方利用這個共享文件來傳遞信息。這個共享文件比較特殊,它不屬於文件系統並且只存在於內存中。另外還有一點,管道採用的是半雙工通信方式的,數據只能在一個方向上流動。
簡單的模型如下所示。
nbXJ2T.png

信號
信號是軟件層次上對中斷機制的一種模擬,是一種異步通信方式,進程不必通過任何操作來等待信號的到達。信號可以在用戶空間進程和內核之間直接交互,內核可以利用信號來通知用戶空間的進程發生了哪些系統事件。信號不適用於信息交換,比較適用於進程中斷控制。
信號量
信號量是一個計數器,用來控制多個進程對共享資源的訪問。它常作爲一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。主要作爲進程間以及同一進程內不同線程之間的同步手段。
消息隊列
消息隊列是消息的鏈表,具有特定的格式,存放在內存中並由消息隊列標識符標識,並且允許一個或多個進程向它寫入與讀取消息。信息會複製兩次,因此對於頻繁或者信息量大的通信不宜使用消息隊列。

共享內存
多個進程可以直接讀寫的一塊內存空間,是針對其他通信機制運行效率較低而設計的。
爲了在多個進程間交換信息,內核專門留出了一塊內存區,可以由需要訪問的進程將其映射到自己的私有地址空間。進程就可以直接讀寫這一塊內存而不需要進行數據的拷貝,從而大大的提高效率。

套接字
套接字是更爲基礎的進程間通信機制,與其他方式不同的是,套接字可用於不同機器之間的進程間通信。

1.2 Android中的IPC機制

Android系統是基於Linux內核的,在Linux內核基礎上,又拓展出了一些IPC機制。Android系統除了支持套接字,還支持序列化、Messenger、AIDL、Bundle、文件共享、ContentProvider、Binder等。Binder會在後面介紹,先來了解前面的IPC機制。
序列化
序列化指的是Serializable/Parcelable,Serializable是Java提供的一個序列化接口,是一個空接口,爲對象提供標準的序列化和反序列化操作。Parcelable接口是Android中的序列化方式,更適合在Android平臺上使用,用起來比較麻煩,效率很高。
Messenger
Messenger在Android應用開發中的使用頻率不高,可以在不同進程中傳遞Message對象,在Message中加入我們想要傳的數據就可以在進程間的進行數據傳遞了。Messenger是一種輕量級的IPC方案並對AIDL進行了封裝。

AIDL
AIDL全名爲Android interface definition Language,即Android接口定義語言。Messenger是以串行的方式來處理客戶端發來的信息,如果有大量的消息發到服務端,服務端仍然一個一個的處理再響應客戶端顯然是不合適的。另外還有一點,Messenger用來進程間進行數據傳遞但是卻不能滿足跨進程的方法調用,這個時候就需要使用AIDL了。

Bundle
Bundle實現了Parcelable接口,所以它可以方便的在不同的進程間傳輸。Acitivity、Service、Receiver都是在Intent中通過Bundle來進行數據傳遞。

文件共享
兩個進程通過讀寫同一個文件來進行數據共享,共享的文件可以是文本、XML、JOSN。文件共享適用於對數據同步要求不高的進程間通信。

ContentProvider
ContentProvider爲存儲和獲取數據了提供統一的接口,它可以在不同的應用程序之間共享數據,本身就是適合進程間通信的。ContentProvider底層實現也是Binder,但是使用起來比AIDL要容易許多。系統中很多操作都採用了ContentProvider,例如通訊錄,音視頻等,這些操作本身就是跨進程進行通信。

2.Linux和Binder的IPC通信原理

在講到Linux的進程通信原理之前,我們需要先了解Liunx中的幾個概念。

njr0qU.png

內核空間和用戶空間
當我們接觸到Liunx時,免不了聽到兩個詞,User space(用戶空間)和 Kernel space(內核空間),那麼它們的含義是什麼呢?
爲了保護用戶進程不能直接操作內核,保證內核的安全,操作系統從邏輯上將虛擬空間劃分爲用戶空間和內核空間。Linux 操作系統將最高的1GB字節供內核使用,稱爲內核空間,較低的3GB 字節供各進程使用,稱爲用戶空間。

內核空間是Linux內核的運行空間,用戶空間是用戶程序的運行空間。爲了安全,它們是隔離的,即使用戶的程序崩潰了,內核也不會受到影響。內核空間的數據是可以進程間共享的,而用戶空間則不可以。比如在上圖進程A的用戶空間是不能和進程B的用戶空間共享的。

進程隔離
進程隔離指的是,一個進程不能直接操作或者訪問另一個進程。也就是進程A不可以直接訪問進程B的數據。

系統調用
用戶空間需要訪問內核空間,就需要藉助系統調用來實現。系統調用是用戶空間訪問內核空間的唯一方式,保證了所有的資源訪問都是在內核的控制下進行的,避免了用戶程序對系統資源的越權訪問,提升了系統安全性和穩定性。

進程A和進程B的用戶空間可以通過如下系統函數和內核空間進行交互。

  • copy_from_user:將用戶空間的數據拷貝到內核空間。
  • copy_to_user:將內核空間的數據拷貝到用戶空間。

內存映射
由於應用程序不能直接操作設備硬件地址,所以操作系統提供了一種機制:內存映射,把設備地址映射到進程虛擬內存區。
舉個例子,如果用戶空間需要讀取磁盤的文件,如果不採用內存映射,那麼就需要在內核空間建立一個頁緩存,頁緩存去拷貝磁盤上的文件,然後用戶空間拷貝頁緩存的文件,這就需要兩次拷貝。
採用內存映射,如下圖所示。

nzlnaV.png
由於新建了虛擬內存區域,那麼磁盤文件和虛擬內存區域就可以直接映射,少了一次拷貝。

內存映射全名爲Memory Map,在Linux中通過系統調用函數mmap來實現內存映射。將用戶空間的一塊內存區域映射到內核空間。映射關係建立後,用戶對這塊內存區域的修改可以直接反應到內核空間,反之亦然。內存映射能減少數據拷貝次數,實現用戶空間和內核空間的高效互動。

2.1 Linux的IPC通信原理

瞭解Liunx中的幾個概念後,就可以學習Linux的IPC通信原理了,如下圖所示。
nzaypq.png
內核程序在內核空間分配內存並開闢一塊內核緩存區,發送進程通過copy_from_user函數將數據拷貝到到內核空間的緩衝區中。同樣的,接收進程在接收數據時在自己的用戶空間開闢一塊內存緩存區,然後內核程序調用 copy_to_user() 函數將數據從內核緩存區拷貝到接收進程。這樣數據發送進程和數據接收進程完成了一次數據傳輸,也就是一次進程間通信。

Linux的IPC通信原理有兩個問題:

  1. 一次數據傳遞需要經歷:用戶空間 --> 內核緩存區 --> 用戶空間,需要2次數據拷貝,這樣效率不高。
  2. 接收數據的緩存區由數據接收進程提供,但是接收進程並不知道需要多大的空間來存放將要傳遞過來的數據,因此只能開闢儘可能大的內存空間或者先調用API接收消息頭來獲取消息體的大小,浪費了空間或者時間。

2.2 Binder的通信原理

Binder是基於開源的OpenBinder實現的,OpenBinder最早並不是由Google公司開發的,而是Be Inc公司開發的,接着由Palm, Inc.公司負責開發。後來OpenBinder的作者Dianne Hackborn加入了Google公司,並負責Android平臺的開發工作,順便把這項技術也帶進了Android。

Binder是基於內存映射來實現的,在前面我們知道內存映射通常是用在有物理介質的文件系統上的,Binder沒有物理介質,它使用內存映射是爲了跨進程傳遞數據。
nzNJUA.png

Binder通信的步驟如下所示。
1.Binder驅動在內核空間創建一個數據接收緩存區。
2.在內核空間開闢一塊內核緩存區,建立內核緩存區和數據接收緩存區之間的映射關係,以及數據接收緩存區和接收進程用戶空間地址的映射關係。
3.發送方進程通過copy_from_user()函數將數據拷貝 到內核中的內核緩存區,由於內核緩存區和接收進程的用戶空間存在內存映射,因此也就相當於把數據發送到了接收進程的用戶空間,這樣便完成了一次進程間的通信。

整個過程只使用了1次拷貝,不會因爲不知道數據的大小而浪費空間或者時間,效率更高。

3.爲什麼要使用Binder

Android是基於Linux內核的 ,Linux提供了很多IPC機制,而Android卻自己設計了Binder來進行通信,主要是因爲以下幾點。
性能方面
性能方面主要影響的因素是拷貝次數,管道、消息隊列、Socket的拷貝次書都是兩次,性能不是很好,共享內存不需要拷貝,性能最好,Binder的拷貝次書爲1次,性能僅次於內存拷貝。
穩定性方面
Binder是基於C/S架構的,這個架構通常採用兩層結構,在技術上已經很成熟了,穩定性是沒有問題的。共享內存沒有分層,難以控制,併發同步訪問臨界資源時,可能還會產生死鎖。從穩定性的角度講,Binder是優於共享內存的。
安全方面
Android是一個開源的系統,並且擁有開放性的平臺,市場上應用來源很廣,因此安全性對於Android 平臺而言極其重要。
傳統的IPC接收方無法獲得對方可靠的進程用戶ID/進程ID(UID/PID),無法鑑別對方身份。Android 爲每個安裝好的APP分配了自己的UID,通過進程的UID來鑑別進程身份。另外,Android系統中的Server端會判斷UID/PID是否滿足訪問權限,而對外只暴露Client端,加強了系統的安全性。
語言方面
Linux是基於C語言,C語言是面向過程的,Android應用層和Java Framework是基於Java語言,Java語言是面向對象的。Binder本身符合面向對象的思想,因此作爲Android的通信機制更合適不過。

從這四方面來看,Linux提供的大部分IPC機制根本無法和Binder相比較,而共享內存只在性能方面優於Binder,其他方面都劣於Binder,這些就是爲什麼Android要使用Binder來進行進程間通信,當然系統中並不是所有的進程通信都是採用了Binder,而是根據場景選擇最合適的,比如Zygote進程與AMS通信使用的是Socket,Kill Process採用的是信號。

4.爲什麼要學習Binder?

Binder機制在Android中的地位舉足輕重,我們需要掌握的很多原理都和Binder有關:

  1. 系統中的各個進程是如何通信的?
  2. Android系統啓動過程
  3. AMS、PMS的原理
  4. 四大組件的原理,比如Activity是如何啓動的?
  5. 插件化原理
  6. 系統服務的Client端和Server端是如何通信的?(比如MediaPlayer和MeidaPlayerService)

上面只是列了一小部分,簡單來說說,比如系統在啓動時,SystemServer進程啓動後會創建Binder線程池,目的是通過Binder,使得在SystemServer進程中的服務可以和其他進程進行通信了。再比如我們常說的AMS、PMS都是基於Binder來實現的,拿PMS來說,PMS運行在SystemServer進程,如果它想要和DefaultContainerService通信(是用於檢查和複製可移動文件的系統服務),就需要通過Binder,因爲DefaultContainerService運行在com.android.defcontainer進程。
還有一個比較常見的C/S架構間通信的問題,Client端的MediaPlayer和Server端的MeidaPlayerService不是運行在一個進程中的,同樣需要Binder來實現通信。

可以說Binder機制是掌握系統底層原理的基石。根據Android系統的分層,Binder機制主要分爲以下幾個部分。

n5i5PP.png

上圖並沒有給出Binder機制的具體的細節,而是先給出了一個概念,根據系統的Android系統的分層,我將Binder機制分爲了Java Binder、Native Binder、Kernel Binder,實際上Binder的內容非常多,完全可以寫一本來介紹,但是對於應用開發來說,並不需要掌握那麼多的知識點,因此本系列主要會講解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


這裏不僅分享大前端、Android、Java等技術,還有程序員成長類文章。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章