BInder机制总结
BInder機制
Linux內核的基礎知識
進程隔離/虛擬地址空間
操作系統當中為了保證進程間互不干擾,設計了進程隔離的技術,避免了一個進程去操作另一個進程的數據。進程隔離用到了虛擬地址空間,不同進程的虛擬地址空間是不同的,不同進程之間數據不共享,進程要和另一個進程通信就需要通過某種進程間的通信機制去進行,在Android當中就是通過Binder機制來完成。
系統調用
Linux內核當中有個很重要的概念叫系統調用,因為對內核會有某種保護機制來告訴應用程序只能訪問某些許可資源,而不許可資源是不能被訪問的,這就是把Linux的內核層和上層應用程序抽象分離開,也就是內核層和用戶空間。用戶可以通過系統調用在用戶空間訪問內核的某些程序。
Binder驅動
在Android當中,BInder運行在內核空間當中的,各個用戶進程通過Binder通信內核來進行交互的模塊叫做Binder驅動。驅動程序一般指的是設備的驅動程序,是一種可以使計算機和設備通信的特殊程序,其實也是一種軟件,但是又相當于硬件接口,操作系統可以通過這個接口來控制硬件設備,這就是Binder驅動,Binder可以理解為一種虛擬的物理設備。
Binder通信機制
為什么使用Binder
Android使用Linux內核,擁有非常多的跨進程通信機制,比如管道、Socket等等,為什么會添加一個BInder通信機制來作為Android特有的進程間通信機制呢?主要是因為性能與安全的優勢。
性能
移動設備上廣泛使用跨進程通信肯定會對通信機制提出嚴格要求,而Binder比傳統的方式更加高效。
安全
傳統的進程間通信對于通信雙方的身份沒有做出嚴格的驗證,只有上層協議才會進行相關保證,比如說Socket通信的話IP地址是客戶端手動填寫的,可以進行人為偽造,而Binder機制支持通信雙方進行身份校驗,這是BInder在安全性方面所做的努力,這大大提高了應用程序的安全性,Binder的身份校驗也是Android權限模型的基礎。
Binder通信模型
其實可以把跨進程通信的雙方,一端稱為服務進程,而另一端稱為客戶端進程,而由于進程隔離的存在,我們沒有辦法通過一般方式在客戶端進程訪問到服務端進程如果過不進行跨進程通信的話。
兩個運行在用戶空間的進程要完成通信必須通過內核的幫助,而這個運行在內核的程序就是Binder驅動。它的功能類似現實生活中雙方打電話所要借助的“通信基站”,那么通訊錄就是ServiceManager。
- 電話基站:Binder驅動
- 通訊錄:serviceManager
Binder通信機制原理
通信的步驟:第一步是ServiceManager的建立,也就是首先要有一個進程向驅動提出申請ServiceManager,而內核驅動以后,ServiceManager就負責管理所有的“電話號碼”也就是通信方,由于此時還沒有需要通信的進程向它注冊,所以最開始是沒有內容的。第二步就是一方要通信另一方就需要將“自己聯系的方式”向ServiceManager中注冊。
Binder是如何進行跨進程通信的呢?
Server端在ServiceManager當中注冊了它的方法,而Client端如果想要調用Server端的一個方法,如果到ServiceManager中去查找時候有這樣的一個方法,這個時候ServiceManager會返回給Client一個代理對象的方法,而這個代理對象的方法是一個空方法,它的主要作用就是Client端調用方法的時候會返回給內核驅動,內核驅動接收到了代理對象的方法,知道了客戶端想要調用的服務端方法,內核回去調用服務端的相應方法,然后服務端的方法調用完后,把結果返回給內核驅動層ServiceManager,ServiceManager才會把結果返回給客戶端。這樣才是一個完整的Binder跨進程通信機制的原理。也就是客戶端進程持有一個服務端進程的代理,通過代理對象協助驅動完成跨進程通信,具體的跨進程通信都是通過代理完成的。
所以什么是Binder?
- 直觀地說Binder是Android當中的一個類,實現了IBinder接口。
- 通常意義下,從IPC角度看,Binder指的是Android當中的一種跨進程的通信機制,當然Binder跨線程也可以。
- Binder還可以理解為是一種虛擬的物理設備,他的設備驅動是/dev/binder,這種通信方式在Linux當中是沒有的;從Android Framework的角度來說,Binder是ServiceManager連接各種Manager(ActivityManager、WindowManager等)和相應ManagerService的橋梁,
- 從Android應用層來說,Binder是客戶端和服務端進行通信的媒介,當bindService的時候,服務端會返回一個包含了服務端業務調用的Binder對象,通過這個Binder對象,客戶端就可以獲取服務端提供的服務或者數據,這個服務包括了普通的服務和基于AIDL的服務。
- 對于傳輸過程而言,Binder是可以進行跨進程傳遞的對象。Binder驅動會對具有跨進程傳遞能力的對象做特殊處理,自動完成代理對象和服務端對象的轉換。
- 對于Service進程來說,Binder指的是Binder本地對象/對于Client來說,Binder指的是代理對象(比如說如果Activity和Service處于不同進程中的話,最后獲取的就是一個Binder代理對象)。
從Framework層看Binder原理
首先,Android利用Binder進行通信的話,肯定要首先獲取Binder對象。
首先系統服務和我們自定義服務Binder對象的獲取方式是不一樣的,原因在于系統服務是在系統啟動的時候就被注冊到了ServiceManager,所以只需要通過ServiceManager.getService(String name)傳入想要獲得的系統服務的名字就能獲得對應的Binder對象進行進程間通信了,系統已經將該Service所提供的服務全部寫好了:系統啟動,init進程會啟動一個叫ServiceManager的進程,這個進程啟動之后會做三件事請:第一通過open打開設備文件/dev/binder,將這個文件當中的內容通過mmap映射到本進程空間當中;第二是通過IO控制命令BINDER_SET_CONTEXT_MGR將當前進程注冊到Binder驅動當中,然后Binder驅動就會為他在內核空間創建一個稱為binder_context_mgr_node的節點,這個節點就是ServiceManager在內核空間Binder實體了,然后將它的句柄設置為0,它的創建時在系統啟動的時候,這個節點在Binder驅動中是唯一的,所以也造成了ServiceManager區別于其他Server了,但是它仍然是運行在用戶空間的;第三ServiceManager啟動之后會無限循環等待Server和Client之間的進程間通信請求了;接著Zygote進程會孵化一個子進程SystemService,我們的大部分系統服務比如ActivityManagerService、WindowManagerService、MediaPlayService等等都是這個進程內的一個線程,這些服務都是會調用ServiceManager.addService方法注冊添加到ServiceManager中的服務列表svlist中,這樣系統服務就都注冊到ServiceManager當中了。
ServiceManager是隨著系統的啟動而創建的,它不同于普通的Server,甚至可以理解為是普通Server的Server,系統會將所要提供的服務注冊到它的服務列表中,服務列表中存儲的就是服務名字和服務在Binder驅動中的句柄,這里的句柄可以理解為同一進程內部的引用,只不過現在是跨進程通信換了個名字而已,如果我們的應用想要使用某個系統服務的話,可以傳入服務的名字調用ServiceManager.getService得到這個服務對應的Binder對象這種方式來進行獲取,然后就可以使用這個Binder對象來進行相應操作了,具體來說這也是ContentProvider為我們封裝的內容了。
Binder就是通過客戶端/服務端模式實現的,關于Binder的實現,還需要區分幾個概念:Server/Client/ServiceManager/Binder驅動,這個部分是Binder架構的核心,尤其是ServiceManager和Binder驅動,因為我們的應用程序安裝到系統上的時候,都會被分配一個唯一的用戶ID,它們是運行在獨立進程當中的,Linux基于安全的考慮是不允許我們直接訪問系統上服務的,因為它們屬于不同的進程,而進程間的數據是不能共享的,那么要使用到這些系統服務就需要通過跨進程通信機制了,也就需要內核空間的參與,所以用到Binder驅動,它的作用相當于是橋梁。平常我們訪問系統服務,比如媒體資源是使用ContentProvider,它的底層實現就是BInder,只是系統為我們做了封裝。一句話總結下Client和Server通信時,ServiceManager相當于通訊錄,而Binder驅動就是通信基站。
具體的通信過程是這樣的:
Server和Binder驅動通信ServiceManager.addService(String name,IBinder service),傳入的內容是Server的名字和Server在Binder驅動當中對應的對象:(1)Server首先將自己作為對象,并且附上一個句柄為0的值(用于訪問ServiceManager),將這些內容封裝成一個數據包,open有關Binder的設備文件/dev/binder,將封裝好的這個數據包發送給Binder驅動;(2)Binder驅動在收到這個數據包之后,發現里面存在一個Server對象,首先會在Binder驅動自己內部新建該Server對應的Binder實體,并且賦予一個大于0的句柄,同時會將這個句柄也加入到數據包當中,接著會將數據包發送到句柄為0對應的對象,也就是ServiceManager了。(3)ServiceManager收到轉發給自己的數據包之后,會查看其服務列表svlist中是否已經存在當前Server名字的服務,如果不存在的話,就會將當前服務+當前服務對應于Binder驅動中的句柄加入到列表當中。 這個過程,系統服務就注冊到ServiceManager當中了。
Client和Binder驅動通信ServiceManager.getService(String name),返回對應的所請求Binder對象,傳入的參數是將要請求的服務的名字:(1)Client首先會將要獲取的服務的名字以及一個句柄為0的值(為了訪問ServiceManager)封裝成一個數據包,open有關Binder的設備文件/dev/binder,將該數據包發送給Binder驅動;(2)Binder驅動在收到數據包之后發現里面有句柄為0的信息,就將該數據包轉發給ServiceManager來進行處理了;(3)ServiceManager在收到數據包之后根據服務名字查看自己服務列表svlist,找到之后會將對應的在Binder驅動當中的句柄信息也封裝成一個數據包;(4)該數據包也會通過Binder驅動被發送給Client端;(5)Client端在收到數據包之后,就得到了自己所請求的服務在Binder驅動中的句柄,它會利用這個句柄信息在自己本地創建一個遠程Server的代理,以后Client發消息都是發給這個代理的,隨后的通信就都變成了代理通過Binder驅動與真正的Server進行交互,去完成跨進程通信。
這就是系統服務怎么注冊到ServiceManager內,以及怎么獲取服務對應于Binder驅動的句柄也就是Binder對象的過程。
而對于自定義的服務想要實現進程間通信的話,Client端和Server端都是需要我們自己實現的,其中Server端的實現是借助于Service來完成的,Android就提供AIDL幫助簡化這個過程,當然就算不用AIDL也是可以實現跨進程通信的,只是序列化和反序列化數據的封裝和相關順序問題需要注意。使用AIDL只需要將Server進程想要提供給Client進程訪問的方法定義在一個.aidl文件中就可,假設IStudentService.aidl,那么系統會為這個AIDL文件自動生成對應的IStudentService.java文件,它的基本結構是這樣的:(1)首先這個接口會繼承于android.os.IInterface接口;(2)會聲明一個名為Stub的內部抽象類,繼承自android.os.Binder,也就是說是一個Binder類,并且實現remote.IStudentService本身這個接口;(3)Stub類包含了一些方法和一個內部代理類Proxy:
remote.IStudentService asInterface(android.os.IBinder obj)
android.os.IBinder asBinder()
boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
class Proxy implements remote.IStudentService
這個代理類實現了IStudentService的方法,但是并不是真正的邏輯實現,而是通過transact進行的一些進程切換操作。
package remote;
public interface IStudentService extends android.os.IInterface {
/**
* Default implementation for IStudentService.
*/
public static class Default implements remote.IStudentService {
@Override
public remote.Student getStudentById(int id) throws android.os.RemoteException {
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements remote.IStudentService {
private static final java.lang.String DESCRIPTOR = "remote.IStudentService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an remote.IStudentService interface,
* generating a proxy if needed.
* !!!!
* 用于將服務端的Binder對象轉換成客戶端所需要的AIDL接口類型對象,這種轉換過程是區分進程的,
* 會根據傳入的Binder對象來判斷是跨進程通信還是進程內部通信:
* 如果是進程內部通信,返回的是IStudentService.Stub對象本身;
* 如果是跨進程通信,返回的就是remote.IStudentService.Stub.Proxy(obj)代理對象,
* 這個代理對象中的方法會通過調用transact方法來進行內核態的切換。
*/
public static remote.IStudentService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof remote.IStudentService))) {
return ((remote.IStudentService) iin);
}
return new remote.IStudentService.Stub.Proxy(obj);
}
//返回當前Stub也就是Binder對象
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 這個方法運行在服務端中的Binder線程池中,當客戶端發起跨進程請求時,遠程請求會通過系統底層封
* 裝后交由此方法處理。通過code確定客戶端所請求的目標方法,接著從data中取出目標方法所需參數
* ,然后執行目標方法。目標方法執行完畢,向reply寫入返回值。
* 而如果這個方法返回false,客戶端的請求就會失敗,所以也可以用這個特性來做權限驗證。
*/
@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_getStudentById: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
remote.Student _result = this.getStudentById(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements remote.IStudentService {
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;
}
/**
* 內部調用transact方法發起RPC請求,進行遠程過程調用,同時當前線程掛起;
* 然后服務端的onTransact方法會被調用,直到RPC過程返回后,當前線程繼續執行。
*/
@Override
public remote.Student getStudentById(int id) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
remote.Student _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);
boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentById, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getStudentById(id);
}
_reply.readException();
if ((0 != _reply.readInt())) {
_result = remote.Student.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static remote.IStudentService sDefaultImpl;
}
static final int TRANSACTION_getStudentById = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(remote.IStudentService impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static remote.IStudentService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public remote.Student getStudentById(int id) throws android.os.RemoteException;
}
自定義服務進行跨進程通信的過程:(1)創建一個AIDL文件,內部定義了服務端進程想要提供給客戶端進程的方法列表,然后系統會生成對應的一個同名Java文件;(2)自定義服務通過Service實現,在里面實現提供給客戶端的方法的具體邏輯,之后客戶端bindService后,在Service的onBind方法返回這個服務端對應的Binder對象,然后客戶端就能在ServiceConnection中onServiceConnected這個回調方法當中獲得服務端對應的Binder對象;(3)客戶端獲得了Binder對象之后會調用例如IStudentService.Stub.asInterface(IBinder service)將Binder對象傳入,獲取remote.IStudentService對象,從這里開始就跟跨進程通信分開了,如果是跨進程通信asInterface返回的是IStudentService.Stub.Proxy代理對象,以后客戶端調用服務端的方法實際上就是調用IStudentService.Stub.Proxy代理對象里面對應于服務端的方法,會通過transact陷入內核態來進行實際上的進程間通信去調用服務端的onTransact方法,在onTransact方法中會根據傳入標志調用不同的服務端方法; 而如果是同進程通信的話,asInterface返回的是IStudentService.Stub對象,然后直接調用服務端的方法,而不必陷入內核態執行了,可以是在Service類當中繼承IStudentService.Stub抽象類實現抽象方法然后在onBind()方法返回這個實現類的實例。
在使用AIDL實現Binder通信的過程中,無論服務端方法還是客戶端方法都是運行在各自的Binder線程池中的,如果我們想要更新UI的話,就需要用到Handler進行切換操作。
AIDL
Binder通信機制的具體應用。
常用系統服務
| 傳入的Name | 返回的對象 | 說明 |
|---|---|---|
| WINDOW_SERVICE | WindowManager | 管理打開的窗口程序 |
| ACTIVITY_SERVICE | ActivityManager | 管理應用程序的系統狀態 |
| NOTIFICATION_SERVICE | NotificationManager | 狀態欄服務 |
| LAYOUT_INFLATER_SERVICE | LayoutManager | 取得XML里面定義的View |
| ALARM_SERVICE | AlarmManager | 鬧鐘服務 |
| POWER_SERVICE | PowerManager | 電源服務 |
獲取示例:
WindowManager windowManager= Context.getSystemService(Context.WINDOW_SERVICE);
參考
BInder系列開篇
Binder原理剖析未看完
總結
以上是生活随笔為你收集整理的BInder机制总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数学建模公选课第二节常微分方程2020.
- 下一篇: java第七章异常