Android進階——Android跨進程通訊機制之Binder、IBinder、Parcel、AIDL

前言

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驅動

五、簡單的總結

將前面的所有概念連接起來理解就會非常好消化知識點:

  1. Linux的虛擬內存機制導致內存的隔離,進而導致進程隔離
  2. 進程隔離的出現導致對內存的操作被劃分爲用戶空間和內核空間
  3. 用戶空間需要跨權限去訪問內核空間,必須使用系統調用去實現
  4. 系統調用需要藉助內核模塊/驅動去完成

前三步決定了進程間通訊需要藉助內核模塊/驅動去實現,而Binder驅動就是內核模塊/驅動中用來實現進程間通訊的

爲什麼要用Binder

Linux提供有管道、消息隊列、信號量、內存共享、套接字等跨進程方式,那爲什麼Android要選擇Binder另起爐竈呢?

一、傳輸性能好

  • Socket:是一個通用接口,導致其傳輸效率低,開銷大
  • 共享內存:雖然在傳輸時不需要拷貝數據,但其控制機制複雜
  • Binder:複雜數據類型傳遞可以複用內存,需要拷貝1次數據
  • 管道和消息隊列:採用存儲轉發方式,至少需要拷貝2次數據,效率低

二、安全性高

  • 傳統的進程:通信方式對於通信雙方的身份並沒有做出嚴格的驗證,只有在上層協議上進行架設
  • Binder機制:從協議本身就支持對通信雙方做身份校檢,因而大大提升了安全性

Binder通信模型

首先在理解模型之前先熟悉這幾個概念:

  • Client進程:跨進程通訊的客戶端(運行在某個進程)
  • Server進程:跨進程通訊的服務端(運行在某個進程)
  • Binder驅動:跨進程通訊的介質
  • ServiceManager:跨進程通訊中提供服務的註冊和查詢(運行在System進程)

這裏寫圖片描述

這裏只是個簡單的模型而已,只需理解模型的通訊流程:

  1. Server端通過Binder驅動在ServiceManager中註冊
  2. Client端通過Binder驅動獲取ServiceManager中註冊的Server端
  3. Client端通過Binder驅動和Server端進行通訊

Binder通信原理

這裏寫圖片描述

理解完模型流程之後,開始理解模型的通訊原理:

  1. Service端通過Binder驅動在ServiceManager的查找表中註冊Object對象的add方法
  2. Client端通過Binder驅動在ServiceManager的查找表中找到Object對象的add方法,並返回proxy對象的add方法,add方法是個空實現,proxy對象也不是真正的Object對象,是通過Binder驅動封裝好的代理類的add方法
  3. 當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();
    }
}   

梳理客戶端的調用流程:

  1. 調用Stub.asInterface獲取BinderProxy對象
  2. 調用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的理解,個人覺得這樣是最好的記憶方式,一起來寫吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章