Android Framework 學習(三):Android 跨進程通信機制


一、跨進程通信方式

跨進程通信主要有以下幾類:管道、Socket、共享內存、信號。

1. 管道

管道的特點是半雙工&單向的,管道里面的數據只能往一個方向流動。一般情況下管道是在父子進程之間使用的。

2. socket

socket的特點是全雙工,即可讀也可寫。可以用在兩個無親緣關系的進程之間,但需要公開路徑。

例子:在Android的Framework機制中,zygote就是通過socket來接受AMS的請求,然后啟動應用進程的。

3. 共享內存

共享內存的特點:速度快,且不需要多次拷貝,且進程之間不需要存在親緣關系,只需要拿到文件描述符即可。

這里補充一下:管道和socket的問題在於數據不能太大,否則性能會非常糟糕,相比較共享內存不存在這個問題。

4. 信號

信號的特點是:

a).單向的,發送出去后不管其他人接受者是如何處理的;

b).只能帶信號,不能帶其他參數。

c).知道進程的pid就可以發信號,而且一次可以一群進程發信號(需 root權限 或 同uid 才行)。

例子:Android的Process#killProcess方法,就是發送的 SIGNAL_KILL 信號。

5. Binder 

Binder機制是Android特有的進程間通信的機制,特點為:采用C/S的通信模式、有更好的傳輸性能,最重要的特點是安全。

Android的四大組件,有時候不同的組件之間所在的進程是不一樣的,當處於不同的進程的時候,就需要進行進程間通信了。這些進程間的通信依賴於Binder IPC機制。不僅如此,Android 系統對應用層提供的服務如:AMS、PMS等都是基於Binder IPC機制實現的。Binder機制在Android系統中的位置非常重要。

二、為何 Android 選用 Binder 作為跨進程通信的解決方案

1. 從性能方面考慮

Socket 作為一款通用接口,其傳輸效率低,開銷大,主要用在跨網絡的進程間通信和本機上進程間的低速通信。管道采用存儲-轉發方式,即數據先從發送方緩存區拷貝到內核開辟的緩存區中,然后再從內核緩存區拷貝到接收方緩存區,至少有兩次拷貝過程。共享內存雖然無需拷貝,但控制復雜,難以使用。Binder 只需要一次數據拷貝,性能上僅次於共享內存。

2. 從穩定性方面考慮

Binder 基於 C/S 架構,客戶端(Client)有什么需求就丟給服務端(Server)去完成,架構清晰、職責明確又相互獨立,自然穩定性更好。共享內存雖然無需拷貝,但是控制負責,難以使用。從穩定性的角度講,Binder 機制是優於內存共享的。

3. 從安全性方面考慮

傳統的 IPC 沒有任何安全措施,完全依賴上層協議來確保。首先傳統的 IPC 接收方無法獲得對方可靠的進程用戶ID/進程ID(UID/PID),從而無法鑒別對方身份。Android 為每個安裝好的 APP 分配了自己的 UID,故而進程的 UID 是鑒別進程身份的重要標志。Android 需要建立一套新的 IPC 機制來滿足系統對穩定性、傳輸性能和安全性方面的要求,這就是 Binder。

三、Binder的通信架構

首先建議先看一下:Linux下進程間通信的原理 和 Binder 跨進程通信原理 這兩篇文章。

Binder的通信架構示例如下圖:

Binder 是基於 C/S 架構的。由一系列的組件組成,包括 Client、Server、ServiceManager、Binder 驅動。其中 Client、Server、Service Manager 運行在用戶空間,Binder 驅動運行在內核空間。其中 Service Manager 和 Binder 驅動由系統提供,而 Client、Server 由應用程序來實現。Client、Server 和 ServiceManager 均是通過系統調用 open、mmap 和 ioctl 來訪問設備文件 /dev/binder,從而實現與 Binder 驅動的交互來間接的實現跨進程通信。

1. 進程如何啟動Binder機制

首先打開Binder驅動,Binder驅動會給進程創立一套檔案。然后將返回的描述符進行內存映射,分配緩存區。最后啟動Binder線程,Binder線程會注冊到Binder驅動,同時線程進入Loop循環,不斷的跟Binder驅動交互。

2. Binder機制的分層架構

Binder機制的分層架構如下圖所示:

因為Binder層Java機制我們通過實踐已經知道其基本邏輯,這里我們了解一下Native層的邏輯,方便我們更深入的理解Binder機制。Binder的Native層的代碼地址:http://androidxref.com/kernel_3.18/xref/drivers/staging/android/binder.c

3. binder.c 主要邏輯方法梳理

在之前我們提到了Binder的通信架構和分層架構,這里我們梳理一下binder.c中比較重要的方法。

1). binder_init 方法 - 驅動初始化

在binder_init 方法中,首先創建名為binder的內核線程,用於處理工作隊列中的任務。

2). binder_open 方法 - 打開設備

在binder_open方法中,執行了如下操作:首先創建了binder_proc結構體實例,然后初始化一系列成員變之后,將binder_proc鏈入binder_procs哈希表中,然后進行相關目錄及文件的創建。

3). binder_mmap 方法 - 內存映射

在上面打開binder后,需要調用mmap進行內存映射,該函數經過系統調用,會調用到binder驅動的biner_mmap函數。

4). binder_ioctl 方法 - 設備控制

octl是Linux中常見的系統調用,它用於對底層設備的一些特性進行控制的用戶態接口,應用程序在調用ioctl進行設備控制時,最后會調用到設備注冊struct_file_operations結構體對象時的鈎子上。

Binder做為Android中進程間高效通信的核心組件,其底層是以misc設備驅動的形式實現的,但它本身並沒有實現read、write操作,所有的控制都是通過ioctl操作來實現。

四、Binder 通信過程的總結

1. 首先,一個進程通過 Binder 驅動將自己注冊成為 ServiceManager;

2. Server 通過驅動向 ServiceManager 中注冊 Binder(Server 中的 Binder 實體),表明可以對外提供服務。驅動為這個 Binder 創建位於內核中的實體節點以及 ServiceManager 對實體的引用,將名字以及新建的引用打包傳給 ServiceManager,ServiceManger 將其填入查找表。

3. Client 通過名字,在 Binder 驅動的幫助下從 ServiceManager 中獲取到對 Binder 實體的引用,通過這個引用就能實現和 Server 進程的通信。

五、Binder通信機制的同步與異步

一般我們聲明AIDL的時候,都是類似如下的方式,此方法是同步方法:

interface IPlayer {
    int getVolume();// 同步,假設執行1秒
}

其實我們也可以使用oneway來修飾AIDL內部的方法,修飾完成后就表示此方法是異步調用的。例如:

interface IPlayer {
    oneway void start();//異步,假設執行2秒
    oneway void stop();//異步,假設執行2秒
    int getVolume();// 同步,假設執行1秒
}

在上面的代碼中,Client端調用IPlayer.start(),Server端的start需要執行2秒,由於定義的接口是異步的,Client端可以快速的執行IPlayer.start(),不會被Server端block住2秒。但是當Client端調用IPlayer. getVolume(),Server端的getVolume需要執行1秒,由於定義的接口是同步的,Client端在執行IPlayer. getVolume()的時候,會被Server端block住1秒。

為什么Android的Binder機制會提供同步/異步的調用機制呢?答:異步調用在不需要Server端的返回值或者狀態的時候,能有效的提高Client的效率。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM