前言
Binder機制是Android系統提供的跨進程通訊機制,這篇文章開始會從Linux相關的基礎概念知識開始介紹,從基礎概念知識中引出Binder機制,歸納Binder機制與Linux系統的跨進程機制的優缺點,接着分析Binder的通信模型和原理,而Binder機制最佳體現就是AIDL,所以在後面會分析AIDL的實現原理,最後簡單的提下AMS的Binder體系,整篇文章中間會穿插有IBinder、Binder、Parcel的介紹,整篇文章閱讀難度不大,不會涉及到framework層的Binder原理,AIDL部分需要有AIDL的使用基礎
基礎概念
基礎概念部分介紹Linux的某些機制,主要想表達Binder驅動的出現的原因,如果對Linux熟悉的可以直接跳過這部分,看第五點即可
一、進程隔離
出於安全考慮,一個進程不能操作另一個進程的數據,進而一個操作系統必須具備進程隔離這個特性。在Linux系統中,虛擬內存機制爲每個進程分配了線性連續的內存空間,操作系統將這種虛擬內存空間映射到物理內存空間,每個進程有自己的虛擬內存空間,進而不能操作其他進程的內存空間,每個進程只能操作自己的虛擬內存空間,只有操作系統纔有權限操作物理內存空間。進程隔離保證了每個進程的內存安全,但是在大多數情形下,不同進程間的數據通訊是不可避免的,因此操作系統必須提供跨進程通信機制
二、用戶空間和內核空間
- 用戶空間:表示進程運行在一個特定的操作模式中,沒有接觸物理內存或設備的權限
- 內核空間:表示獨立於普通的應用程序,可以訪問受保護的內存空間,也有訪問底層硬件設備的所有權限
三、系統調用/內核態/用戶態
抽象來看,操作系統中安全邊界的概念就像環路的概念一樣(前提是系統支持這種特性),一個環上持有一個特定的權限組,Intel 支持四層環,但是 Linux 只使用了其中的兩環(0號環持有全部權限,3號環持有最少權限,1號和2號環未使用),系統進程運行在1號環,用戶進程運行在3號環,如果一個用戶進程需要其他高級權限,其必須從3號環過渡成0號環,過渡需要通過一個安全參數檢查的網關,這種過渡被稱爲系統調用,在執行過程中會產生一定數量的計算開銷。所以,用戶空間要訪問內核空間的唯一方式就是系統調用(System Call)
四、內核模塊/驅動
通過系統調用,用戶空間可以訪問內核空間,它是怎麼做到訪問內核空間的呢?Linux的動態可加載內核模塊機制解決了這個問題,模塊是具有獨立功能的程序,它可以被單獨編譯,但不能獨立運行。這樣,Android系統可以通過添加一個內核模塊運行在內核空間,用戶進程之間的通過這個模塊作爲橋樑,就可以完成通信了。在Android系統中,這個運行在內核空間的,負責各個用戶進程通過Binder通信的內核模塊叫做Binder驅動
五、簡單的總結
將前面的所有概念連接起來理解就會非常好消化知識點:
- Linux的虛擬內存機制導致內存的隔離,進而導致進程隔離
- 進程隔離的出現導致對內存的操作被劃分爲用戶空間和內核空間
- 用戶空間需要跨權限去訪問內核空間,必須使用系統調用去實現
- 系統調用需要藉助內核模塊/驅動去完成
前三步決定了進程間通訊需要藉助內核模塊/驅動去實現,而Binder驅動就是內核模塊/驅動中用來實現進程間通訊的
爲什麼要用Binder
Linux提供有管道、消息隊列、信號量、內存共享、套接字等跨進程方式,那爲什麼Android要選擇Binder另起爐竈呢?
一、傳輸性能好
- Socket:是一個通用接口,導致其傳輸效率低,開銷大
- 共享內存:雖然在傳輸時不需要拷貝數據,但其控制機制複雜
- Binder:複雜數據類型傳遞可以複用內存,需要拷貝1次數據
- 管道和消息隊列:採用存儲轉發方式,至少需要拷貝2次數據,效率低
二、安全性高
- 傳統的進程:通信方式對於通信雙方的身份並沒有做出嚴格的驗證,只有在上層協議上進行架設
- Binder機制:從協議本身就支持對通信雙方做身份校檢,因而大大提升了安全性
Binder通信模型
首先在理解模型之前先熟悉這幾個概念:
- Client進程:跨進程通訊的客戶端(運行在某個進程)
- Server進程:跨進程通訊的服務端(運行在某個進程)
- Binder驅動:跨進程通訊的介質
- ServiceManager:跨進程通訊中提供服務的註冊和查詢(運行在System進程)
這裏只是個簡單的模型而已,只需理解模型的通訊流程:
- Server端通過Binder驅動在ServiceManager中註冊
- Client端通過Binder驅動獲取ServiceManager中註冊的Server端
- Client端通過Binder驅動和Server端進行通訊
Binder通信原理
理解完模型流程之後,開始理解模型的通訊原理:
- Service端通過Binder驅動在ServiceManager的查找表中註冊Object對象的add方法
- Client端通過Binder驅動在ServiceManager的查找表中找到Object對象的add方法,並返回proxy對象的add方法,add方法是個空實現,proxy對象也不是真正的Object對象,是通過Binder驅動封裝好的代理類的add方法
- 當Client端調用add方法時,Client端會調用proxy對象的add方法,通過Binder驅動去請求ServiceManager來找到Service端真正對象,然後調用Service端的add方法
Binder對象和Binder驅動
- Binder對象:Binder機制中進行進程間通訊的對象,對於Service端爲Binder本地對象,對於Client端爲Binder代理對象
- Binder驅動:Binder機制中進行進程間通訊的介質,Binder驅動會對具有跨進程傳遞能力的對象做特殊處理,自動完成代理對象和本地對象的轉換
由於Binder驅動會對具有跨進程傳遞能力的對象做特殊處理,自動完成代理對象和本地對象的轉換,因此在驅動中保存了每一個跨越進程的Binder對象的相關信息,Binder本地對象(或Binder實體)保存在binder_node的數據結構,Binder代理對象(或Binder引用/句柄)保存在binder_ref的數據結構
Java層的Binder
- Binder類:是Binder本地對象
- BinderProxy類:是Binder類的內部類,它代表遠程進程的Binder對象的本地代理
- Parcel類:是一個容器,它主要用於存儲序列化數據,然後可以通過Binder在進程間傳遞這些數據
- IBinder接口:代表一種跨進程傳輸的能力,實現這個接口,就能將這個對象進行跨進程傳遞
- IInterface接口:client端與server端的調用契約,實現這個接口,就代表遠程server對象具有什麼能力,因爲IInterface接口的asBinder方法的實現可以將Binder本地對象或代理對象進行返回
Binder類和BinderProxy類都繼承自IBinder,因而都具有跨進程傳輸的能力,在跨越進程的時候,Binder驅動會自動完成這兩個對象的轉換。IBinder是遠程對象的基本接口,是爲高性能而設計的輕量級遠程調用機制的核心部分,但它不僅用於遠程調用,也用於進程內調用。IBinder接口定義了與遠程對象交互的協議,建議不要直接實現這個接口,而應該從Binder派生。Binder實現了IBinder接口,但是一般不需要直接實現此類,而是跟據你的需要由開發包中的工具生成,這個工具叫aidi。你通過aidi語言定義遠程對象的方法,然後用aidi工具生成Binder的派生類,然後使用它
AIDL
由於編譯工具會給我們生成一個Stub的靜態內部類,這個類繼承了Binder, 說明它是一個Binder本地對象,它實現了IInterface接口,表明它具有遠程Server承諾給Client的能力
一、服務端
在服務端中,我們只要實現Stub抽象類,和實現其方法即可
private IBinder myS = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int add(int num1, int num2) throws RemoteException {
Log.i("Hensen", "從客戶端發來的AIDL請求:num1->" + num1 + "::num2->" + num2);
return num1 + num2;
}
};
二、客戶端
在客戶端中,可以通過bindService的回調中獲取AIDL接口
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
public void add(View view) {
try {
int res = iMyAidlInterface.add(1, 2);
Log.i("Hensen", "從服務端調用成功的結果:" + res);
} catch (RemoteException e) {
e.printStackTrace();
}
}
梳理客戶端的調用流程:
- 調用Stub.asInterface獲取BinderProxy對象
- 調用BinderProxy對象的add方法
三、分析原理
1、Stub
Stub類繼承自Binder,意味着這個Stub其實自己是一個Binder本地對象,然後實現了IMyAidlInterface接口,IMyAidlInterface本身是一個IInterface,因此他攜帶某種客戶端需要的能力(這裏是方法add)。此類有一個內部類Proxy,也就是Binder代理對象
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\workspace5\\Boke\\app\\src\\main\\aidl\\com\\handsome\\boke\\IMyAidlInterface.aidl
*/
package com.handsome.boke;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.handsome.boke.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.handsome.boke.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.handsome.boke.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {
return ((com.handsome.boke.IMyAidlInterface) iin);
}
return new com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.handsome.boke.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int add(int num1, int num2) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int add(int num1, int num2) throws android.os.RemoteException;
}
2、asInterface
當客戶端bindService的onServiceConnecttion的回調裏面,通過asInterface方法獲取遠程的service的。其函數的參數IBinder類型的obj,這個對象是驅動給我們的,如果是Binder本地對象,那麼它就是Binder類型,如果是Binder代理對象,那就是BinderProxy類型。asInterface方法中會調用queryLocalInterface,查找Binder本地對象,如果找到,說明Client和Server都在同一個進程,這個參數直接就是本地對象,直接強制類型轉換然後返回,如果找不到,說明是遠程對象那麼就需要創建Binder代理對象,讓這個Binder代理對象實現對於遠程對象的訪問
/**
* Cast an IBinder object into an com.handsome.boke.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {
return ((com.handsome.boke.IMyAidlInterface) iin);
}
return new com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj);
}
3、add
當客戶端調用add方法時,首先用Parcel把數據序列化,然後調用mRemote.transact方法,mRemote就是new Stub.Proxy(obj)傳進來的,即BinderProxy對象
@Override
public int add(int num1, int num2) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
4、transact
BinderProxy的transact方法是native方法,它的實現在native層,它會去借助Binder驅動完成數據的傳輸,當完成數據傳輸後,會喚醒Server端,調用了Server端本地對象的onTransact函數
public native boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
5、onTransact
在Server進程裏面,onTransact根據調用code會調用相關函數,接着將結果寫入reply並通過super.onTransact返回給驅動,驅動喚醒掛起的Client進程裏面的線程並將結果返回
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
AMS的Binder體系
AMS是Android中最核心的服務,主要負責系統中四大組件的啓動、切換、調度及應用進程的管理和調度等工作,從圖中可以看出:
AMS | Binder角色 |
---|---|
IActivityManager | IInterface |
ActivityManagerNative | Binder本地對象 |
ActivityManagerProxy | Binder代理對象 |
ActivityManagerService | Service端 |
ActivityManager | 普通管理類 |
結語
這裏只是簡單的理解下Binder機制的基本原理,後續有時間會研究framework層的知識,如果有興趣的同學可以不依賴AIDL工具,手寫遠程Service完成跨進程通信,這樣就可以加深對AIDL和Binder的理解,個人覺得這樣是最好的記憶方式,一起來寫吧