Binder深入理解
-
背景知識
-
進程隔離:進程隔離是為保護操作系統(tǒng)中進程互不干擾而設計的一組不同硬件和軟件的技術雨席。這個技術是為了避免進程A寫入進程B的情況發(fā)生。進程隔離的實現(xiàn)捌显,使用了虛擬機地址空間茁彭。進程A的虛擬地址和進程B的虛擬地址不同,這樣就防止進程A將數(shù)據(jù)寫入進程B扶歪。
操作系統(tǒng)不同進程之間理肺,數(shù)據(jù)不共享。因此一個進程要與另外一個進程通信,需要某種機制才能完成妹萨。
用戶空間/內(nèi)核空間:Linux內(nèi)核是操作系統(tǒng)的核心年枕,所以為了它的安全,不可能允許其他的應用程序隨便調(diào)用或訪問乎完。所以它提供了一個保護機制熏兄,這個保護機制把內(nèi)核和上層應用程序抽象的隔離開,分別為內(nèi)核空間和用戶空間囱怕。
內(nèi)核態(tài)/用戶態(tài):用戶空間訪問內(nèi)核空間的唯一方式是系統(tǒng)調(diào)用霍弹,這是讓用戶空間對內(nèi)核空間的訪問通過一個統(tǒng)一的入口,這樣所有的資源(如訪問文件)訪問都在內(nèi)核的控制下娃弓,以免用戶程序?qū)ο到y(tǒng)資源的越權訪問典格,從而保障了系統(tǒng)的安全和穩(wěn)定。當一個任務(進程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時台丛,就稱之為內(nèi)核態(tài)耍缴。當進程在執(zhí)行用戶自己的代碼時,則稱之為用戶態(tài)挽霉。
-
內(nèi)核模塊/驅(qū)動:通過系統(tǒng)調(diào)用用戶空間可以訪問內(nèi)核空間防嗡,但是用戶空間之間的相互通信還是不行的,那么就需要系統(tǒng)內(nèi)核的支持侠坎,如Socket,管道等都是內(nèi)核支持的蚁趁。但是Binder并不是Linux內(nèi)核的一部分。需要Linux的動態(tài)可加載內(nèi)核模塊來解決這個問題实胸。用戶進程之間通過這個模塊作為橋梁他嫡,就可以相互通信了。在Android系統(tǒng)中庐完,這個運行在內(nèi)核空間钢属,負責各個用戶進程通過Binder通信的模塊叫做Binder驅(qū)動。
動態(tài)可加載內(nèi)核是指模塊可以在系統(tǒng)運行期間加載到內(nèi)核或從內(nèi)核卸載门躯。模塊是具有獨立功能的程序淆党,它可以被單獨編譯,但不能獨立運行讶凉。它在運行時被鏈接到內(nèi)核作為內(nèi)核的一部分在內(nèi)核空間中運行染乌。模塊通常由一組函數(shù)和數(shù)據(jù)結構組成。
-
-
使用Binder的原因:
- 穩(wěn)定性:Binder采用C/S的通信模式懂讯,客戶端有什么需求直接交給服務端去完成慕匠,架構清晰,職責明確又相互獨立域醇,自然穩(wěn)定性更好台谊。在Linux通信機制中蓉媳,目前只有socket支持C/S的通信模式,但是Socket有其劣勢
- 高效性:Socket是一個通用接口锅铅,導致其傳輸效率低酪呻,開銷大;管道和消息隊列因為采用存儲轉發(fā)方式盐须,所以至少需要拷貝2次數(shù)據(jù)玩荠,效率低;共享內(nèi)存雖然傳輸時沒有拷貝數(shù)據(jù)贼邓,不是C/S模式阶冈,控制機制復雜。Binder只拷貝一次
- 安全性:Linux的IPC機制在本身的實現(xiàn)中塑径,并沒有安全措施女坑,傳統(tǒng)的進程通訊方式對與通信雙方的身份并沒有嚴格的驗證,只有在上層協(xié)議上進行架設统舀,比如Socket通信IP地址是客戶端手動填入的匆骗,可以進行偽造。而Binder機制的UID/PID是由Binder機制本身在內(nèi)核空間添加身份標識誉简,因而大大提高了安全性碉就。
-
Binder的通信模型:
Binder采用C/S通信模式。那么通信的2端可以分為Server進程闷串,Client進程瓮钥。由于進程隔離,它們之間沒法直接進行通信烹吵。需要一定的機制幫助他們進行通信碉熄。
假如將2端之間的通訊看成A給B打電話。那么首先A要知道B的電話號碼年叮,其次撥號以后需要基站將信息傳播出去。那么將有4個角色玻募,分別是發(fā)起通話的Client只损,通訊錄ServerManager(SM),基站Binder驅(qū)動驅(qū)動,接電話的Server七咧。那么整個通訊過程如下:
- 建立通訊錄ServerManager跃惫。首先有一個進程向驅(qū)動提出申請為SM,驅(qū)動同意后艾栋,SM進程負責管理Service爆存。這時還沒有任何Server進行注冊,所以通訊錄還是空的
- 各個Server向SM進行注冊蝗砾。每個Server端啟動后先较,在SM進行注冊携冤,SM會建立一張表,并把這些Server的名字和地址存入其中闲勺。
- Client要與某個Server進行通訊曾棕,那么會去SM里查詢這個Server的地址,然后開始通訊菜循。
以上就是進程間通訊的簡單過程翘地,Server端將自己注冊到SM中,Client要與哪個Server進行通訊癌幕,就去SM里找對應的地址衙耕,這時打電話的雙方號碼都有了,就差一個基站幫忙發(fā)送信息了勺远。這個基站就是Binder驅(qū)動橙喘,它的原理是整個跨進程通訊的核心。
-
Binder機制跨進程原理
上面分析了Binder的通訊過程谚中,建立聯(lián)系后通訊由Binder驅(qū)動來完成渴杆,那么再來看Binder驅(qū)動完成通訊的過程。
假如Client進程要調(diào)用Server進程的Object對象的一個方法get(),那么具體過程如下:
Server進程向SM進行注冊宪塔,告訴SM它的地址磁奖,及它有一個Object對象,可以執(zhí)行get()操作
-
Client向SM進行查詢Server的Object對象某筐。這時Binder驅(qū)動并不會返回一個真正的Object對象比搭,而是返回一個代理對象ObjectProxy。這時連接已經(jīng)建立南誊,然后準備開始通訊身诺。Client進程拿到ObjectProxy后調(diào)用它的get(),這時Binder驅(qū)動收到這個調(diào)用請求,發(fā)現(xiàn)ObjectProxy是Server的Object代理抄囚,于是Binder驅(qū)動通知Server進程調(diào)用它的get(),Server收到消息后調(diào)用自己的get()并把結果返回給Binder驅(qū)動霉赡,Binder驅(qū)動再把結果返回給Client。
可以看出Binder跨進程實際并不是真正把一個對象傳輸?shù)搅肆硗庖粋€進程幔托。Client只是對代理對象的操作穴亏,然后Binder驅(qū)動又把對代理對象的操作告訴Server,讓Server執(zhí)行真正的操作重挑,并把結果返回給Binder驅(qū)動嗓化,Binder驅(qū)動再通過代理對象把結果返回給Client。
SM和Server通常也不在一個進程谬哀,所以Server注冊的過程其實也是跨進程通訊刺覆。SM內(nèi)部已經(jīng)有一個默認地址,Server通過這個默認地址查找到SM史煎。Server存在SM里的對象其實也是一個代理對象谦屑。Server的本地對象只有一個驳糯,其他都是代理對象。
-
總結
- Binder是一種通訊機制伦仍,ALDL使用的就是Binder進行通訊
- 對于Server來說Binder指的是Binder本地對象
- 對于Client來說结窘,Binder指的是Binder代理對象,它只是Binder本地對象的一個遠程代理充蓝。對于這個代理對象的操作隧枫,最終會通過Binder驅(qū)動轉發(fā)到Binder本地對象上完成。
- 對于傳輸過程來說谓苟,Binder驅(qū)動會對代理對象和本地對象進行自動轉換處理
- 要完成本地Binder和代理BInder的轉換官脓,那么Binder驅(qū)動里必然保存了每一個跨進程Binder對象的相關信息,在Binder驅(qū)動中涝焙,Binder本地對象的代表是binder_node的數(shù)據(jù)結構卑笨,Binder代理對象是用binder_ref代表的
AIDL
Java為了方便進行進程進程間通訊,對Binder的通訊過程進行了封裝仑撞,這就是AIDL赤兴,
AIDL的使用:
-
Server端:
-
定義Server端的aidl接口,此接口主要定義可以對外提供的服務
// IServer.aidl package com.madnessxiong.server; interface IServer { void get(); }
-
編譯生成aidl接口實現(xiàn)文件隧哮,這一步主要是生成Binder對象桶良。
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /work/project/Server/app/src/main/aidl/com/madnessxiong/server/IServer.aidl */ package com.madnessxiong.server; // Declare any non-default types here with import statements public interface IServer extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.madnessxiong.server.IServer { private static final java.lang.String DESCRIPTOR = "com.madnessxiong.server.IServer"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.madnessxiong.server.IServer interface, * generating a proxy if needed. */ public static com.madnessxiong.server.IServer asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.madnessxiong.server.IServer))) { return ((com.madnessxiong.server.IServer)iin); } return new com.madnessxiong.server.IServer.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 { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_get: { data.enforceInterface(descriptor); this.get(); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); } } } private static class Proxy implements com.madnessxiong.server.IServer { 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 get() 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_get, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_get = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public void get() throws android.os.RemoteException; }
-
創(chuàng)建Service,在Service中返回IBinder對象,這個IBinder是系統(tǒng)生成的IServer.Stub()沮翔。
public class ClientService extends Service { @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. return iBinder; } IBinder iBinder= new IServer.Stub(){ @Override public void get() throws RemoteException { } }; } //在manifests注冊service <service android:name=".ClientService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.madnessxiong.server.server" /> </intent-filter> </service>
-
啟動Service提供服務
Intent intent = new Intent(this, ClientService.class); startService(intent);
-
-
Client端:
-
將Server端的aidl文件復制到Client端,包名要和Server端保持一致
interface IServer { void get(); }
-
編譯陨帆,生成aidl實現(xiàn)接口文件
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /work/project/Client/app/src/main/aidl/com/madnessxiong/server/IServer.aidl */ package com.madnessxiong.server; // Declare any non-default types here with import statements public interface IServer extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.madnessxiong.server.IServer { private static final java.lang.String DESCRIPTOR = "com.madnessxiong.server.IServer"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.madnessxiong.server.IServer interface, * generating a proxy if needed. */ public static com.madnessxiong.server.IServer asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.madnessxiong.server.IServer))) { return ((com.madnessxiong.server.IServer)iin); } return new com.madnessxiong.server.IServer.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 { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_get: { data.enforceInterface(descriptor); this.get(); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); } } } private static class Proxy implements com.madnessxiong.server.IServer { 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 get() 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_get, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_get = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public void get() throws android.os.RemoteException; }
-
建立ServiceConnection
public class ClientConnection implements ServiceConnection { private IServer iServer; @Override public void onServiceConnected(ComponentName name, IBinder service) { iServer = IServer.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } public IServer getiServer() { return iServer; } }
在onServiceConnected使用 IServer.Stub.asInterface返回IBinder代理對象。
-
綁定服務采蚀,建立連接
public void bindService() { Intent intent = new Intent(); intent.setAction("com.madnessxiong.server.server"); intent.setPackage("com.madnessxiong.server"); clientConnection = new ClientConnection(); bindService(intent, clientConnection, Context.BIND_AUTO_CREATE); }
-
開始跨進程通訊
clientConnection.getiServer().get();
以上就是簡單的AIDL的整個通信過程疲牵。
-
AIDL原理分析:
根據(jù)Binder原理分析,需要4個角色榆鼠,Server端纲爸,本地Binder對象,Client端妆够,代理Binder對象识啦。
-
Server端是提供服務的進程,那么再看下一它的本地Binder對象
IBinder iBinder= new IServer.Stub(){ @Override public void get() throws RemoteException { } };
可以看到是通過IServer.Stub返回的责静,這個是編譯后自動生成的袁滥,看一下它的實現(xiàn):
public static abstract class Stub extends android.os.Binder implements com.madnessxiong.server.IServer { }
可以看到Stub繼承自Binder盖桥,意味著Stub是一個本地Binder對象灾螃。也就是它具有進程間通訊的能力。同時實現(xiàn)了IServer接口揩徊,這個接口代表能為客戶端提供的服務窟哺。
-
Client是調(diào)用服務的進程搓劫,那么看一下它的代理Binder對象
iServer = IServer.Stub.asInterface(service);
可以看到是通過asInterface()獲得本地代理Binder對象的叫榕,看一下它的實現(xiàn)
public static com.madnessxiong.server.IServer asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } //嘗試查找本地代理對象 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); //根據(jù)本地代理對象是否存在,判斷是否是當前進程姜挺,如果是當前進程則返回本地Binder對象 if (((iin != null) && (iin instanceof com.madnessxiong.server.IServer))) { return ((com.madnessxiong.server.IServer) iin); } //如果不是當前進程,那么代表進程間通訊彼硫,則返回代理Binder對象 return new com.madnessxiong.server.IServer.Stub.Proxy(obj); }
-
獲得代理對象后炊豪,那么開始通信:
clientConnection.getiServer().get();
可以看到調(diào)用了代理對象的get(),看一下代碼:
@Override public void get() 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_get, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }
在這里首先對數(shù)據(jù)進行了序列化,然后調(diào)用了transact(),這是一個本地方法拧篮,最終會調(diào)用到talkWithDriver方法词渤,看名字就知道通訊過程交給了驅(qū)動完成,這個方法最后通過ioctl系統(tǒng)調(diào)用串绩,Client進程進入內(nèi)核態(tài)缺虐,Client調(diào)用get()的線程掛起等待返回。驅(qū)動然后去喚醒Server進程礁凡,調(diào)用Server本地Binder對象的onTransact()(實際上由Server端線程池完成),那么再看本地Binder對象的onTransact():
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_get: { data.enforceInterface(descriptor); this.get(); reply.writeNoException(); return true; } default: { return super.onTransact(code, data, reply, flags); } } }
在這里根據(jù)調(diào)用號(每個AIDL方法都有一個編號高氮,在跨進程的時候,不會傳遞方法顷牌,而是傳遞編號指明調(diào)用哪個方法)剪芍,這里將調(diào)用本地的get(),然后返回給驅(qū)動韧掩。驅(qū)動喚醒掛起的Client線程紊浩,將結果返回。這樣就完成了一次進程間通訊疗锐。
總結:
角色:以上就是AIDL的跨進程通訊過程坊谁,可以看到和Binder分析里的基本一致,Client及代理Binder對象滑臊,Server及本地Binder對象口芍,Stub繼承Binder接口進行進程間通訊功能。
過程:代理Binder對象通過Binder驅(qū)動調(diào)用方法雇卷,Binder驅(qū)動通知本地Binder調(diào)用方法返回結果鬓椭,Binder驅(qū)動將結果返回給代理Binder,這樣就完成了一次進程間通訊关划。