Android進階(六)Binder機制

猥瑣發育_別浪發表於2019-02-07

一、程式通訊

1、程式隔離:

程式隔離是為保護作業系統中程式互不干擾而設計的一組不同硬體和軟體的技術。程式資料不共享,程式A的虛擬地址和程式B的虛擬地址不同,這樣就防止程式A將資料資訊寫入程式B,保證了資料的安全性。

Android進階(六)Binder機制

  • 程式空間分為核心空間和使用者空間,核心空間(Kernel)是系統核心執行的空間。使用者空間(User Space)是使用者程式執行的空間,他們之間是隔離的。
  • 核心有訪問的所有許可權,使用者空間也可以通過系統介面去訪問核心空間。使用者空間可以通過核心空間(類似於中介者)進行相互訪問。

2、Linux程式間IPC方式:

  • 管道
  • 訊息佇列
  • 共享記憶體
  • 套接字
  • 訊號量
  • 訊號

Linux 下的傳統 IPC 通訊原理:

Android進階(六)Binder機制
傳輸過程:
記憶體快取區 --> 核心快取區 --> 記憶體快取區,需要 2 次資料拷貝;

3、Binder機制的特點

Android進階(六)Binder機制
(1)傳輸效能

  • Binder資料拷貝只需要一次,而訊息佇列、管道、Socket等需要兩次,共享記憶體一次拷貝都不需要。Binder效能僅次於共享記憶體。

(2)穩定性

  • Binder基於C/S架構,Server端和Client端相對獨立,穩定性好。
  • 共享記憶體沒有Server端和Client端的區分,可能存在同步死鎖等問題。Binder穩定性優於共享記憶體。

(3)安全性

  • 傳統的Linux通訊方式無法獲取對方程式的UID,所以訪問接入點是開放的,不安全。
  • Android為每個應用程式分配了自己的UID,作為自身的標識。Binder的方式可以通過UID建立私有通道,Binder的安全性更高。

二、Binder機制實現原理

1、記憶體對映

Binder IPC正是基於記憶體對映(mmap)來實現的

Android進階(六)Binder機制
Binder通訊過程:

  • 首先 Binder 驅動在核心空間建立一個資料接收快取區;
  • 接著在核心空間開闢一塊核心快取區,建立核心快取區和核心中資料接收快取區之間的對映關係,以及核心中資料接收快取區和接收程式使用者空間地址的對映關係;
  • 傳送方程式通過系統呼叫 copy_from_user() 將資料 copy 到核心中的核心快取區,由於核心快取區和接收程式的使用者空間存在記憶體對映,因此也就相當於把資料傳送到了接收程式的使用者空間,這樣便完成了一次程式間的通訊。

2、Binder通訊模型

Android進階(六)Binder機制

  • 包括 Client、Server、ServiceManager、Binder 驅動。
  • 其中 Client、Server、Service Manager 執行在使用者空間,Binder 驅動執行在核心空間。
  • 對於Client,Binder是Server本地物件的一個引用,這個引用實際上是一個代理物件,Client通過這個代理物件來間接訪問Server的本地物件。
  • 對於Server,Binder是提供具體實現的本地物件,需向ServiceManager註冊。
  • Binder驅動是連線Client來Server的橋樑,負責將代理物件轉化為本地物件,並將Server的執行結果返回給Client。
  • ServiceManager它儲存了Server Binder字元名稱和Binder引用的對映,Client通過它來找到Server的Binder引用。

3、Binder通訊過程:

  • 一個程式使用 BINDERSETCONTEXT_MGR 命令通過 Binder 驅動將自己註冊成為 ServiceManager;
  • Server 通過驅動向 ServiceManager 中註冊 Binder,表明可以對外提供服務。驅動為這個 Binder 建立位於核心中的實體節點以及 ServiceManager 對實體的引用,將名字以及新建的引用打包傳給 ServiceManager,ServiceManger 將其填入查詢表。
  • Client 通過名字,在 Binder 驅動的幫助下從 ServiceManager 中獲取到對 Binder 實體的引用,通過這個引用就能實現和 Server 程式的通訊。

三、AIDL

1、AIDL使用的基本步驟

(1)生成AIDL介面(new->AIDL->AIDL File)

interface MyWindowManager {
    void sysout();
}
複製程式碼

生成AIDL檔案之後,比起以前多了一個叫做 aidl 的包,而且他的層級是和 java 包相同的。
(2)編譯MyWindowManager.aidl生成Java檔案

public interface MyWindowManager extends android.os.IInterface {
  /** Local-side IPC implementation stub class. */
  //Stub 繼承 Binder, 說明它是一個 Binder 本地物件;實現 IInterface 介面,表明Server可以提供的方法
  public static abstract class Stub extends android.os.Binder
      implements com.example.myview.binder.MyWindowManager {
    private static final java.lang.String DESCRIPTOR = "com.example.myview.binder.MyWindowManager";

    /** Construct the stub at attach it to the interface. */
    public Stub() {
      this.attachInterface(this, DESCRIPTOR);
    }

    public static com.example.myview.binder.MyWindowManager asInterface(android.os.IBinder obj) {
      if ((obj == null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin != null) && (iin instanceof com.example.myview.binder.MyWindowManager))) {
        return ((com.example.myview.binder.MyWindowManager) iin);
      }
      return new com.example.myview.binder.MyWindowManager.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 {
      ......
      return super.onTransact(code, data, reply, flags);
    }

    //Binder本地代理物件
    private static class Proxy implements com.example.myview.binder.MyWindowManager {
      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;
      }

      @Override
      public void sysout() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          mRemote.transact(Stub.TRANSACTION_sysout, _data, _reply, 0);
          _reply.readException();
        } finally {
          _reply.recycle();
          _data.recycle();
        }
      }
    }

    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_sysout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
  }

  public void sysout() throws android.os.RemoteException;
}
複製程式碼
  • MyWindowManager繼承了IInterface,是Client和Server通訊的介面
  • Stub為靜態抽象內部類,繼承了Binder。其子類需要實現MyWindowManager介面,是Server的Binder的本地物件
  • Stub.Proxy為靜態內部類,內部包含了IBinder物件,是Server在Client中的本地代理物件,將引數序列化後交給mRemote處理,實現了跟遠端Stub的通訊
  • asInterface方法通常是Client在bindService成功後,由Client來呼叫的,作用是將繫結成功後返回的IBinder物件轉換為具體的IInterface介面。Client拿到這個IInterface介面後跟Server進行通訊

(3)Server端提供方法的具體實現

public class MyWindowManagerService extends Service {
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    return mWindowManager;
  }

  private final MyWindowManager.Stub mWindowManager = new MyWindowManager.Stub() {
    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble,
        String aString) throws RemoteException {

    }

    @Override
    public void sysout() throws RemoteException {
      Log.e("hj", "sysout: " );
    }
  };

}
複製程式碼

(4)其他程式的Activity實現跟Service的通訊

public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Intent intent = new Intent(this, MyWindowManagerService.class);
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
  }

  ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      MyWindowManager windowManager = MyWindowManager.Stub.asInterface(service);
      try {
        windowManager.sysout();
      } catch (RemoteException e) {
        e.printStackTrace();
      }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
  };
}
複製程式碼

2、IBinder/IInterface/Binder/BinderProxy/Stub

  • IBinder : IBinder 是一個介面,代表了一種跨程式通訊的能力。只要實現了這個藉口,這個物件就能跨程式傳輸。
  • IInterface : IInterface 代表的就是 Server 程式物件提供了哪些方法
  • Binder : Java 層的 Binder 類,代表的其實就是 Binder 本地物件。Proxy 類是 Binder 類的一個內部類,它代表遠端程式的 Binder 物件的本地代理;這兩個類都繼承自 IBinder, 因而都具有跨程式傳輸的能力;實際上,在跨越程式的時候,Binder 驅動會自動完成這兩個物件的轉換。
  • Stub : AIDL 的時候,編譯工具會給我們生成一個名為 Stub 的靜態內部類;這個類繼承了 Binder, 說明它是一個 Binder 本地物件,它實現了 IInterface 介面,表明它具有 Server 承諾給 Client 的能力;Stub 是一個抽象類,具體的 IInterface 的相關實現需要自己實現。

參考文章:
Android Binder設計與實現 - 設計篇
為什麼 Android 要採用 Binder 作為 IPC 機制?
寫給 Android 應用工程師的 Binder 原理剖析

相關文章