Android跨程序通訊Binder原理分析(一)
文章目錄
1. Linux程序基礎
Android系統是基於Linux核心的作業系統,在學習Binder通訊之前需要了解一些Linux系統的基本概念
1.1 程序隔離
程序隔離是作業系統核心對於資源管理和安全增強的特性,其最終的目的是讓作業系統能夠更好的控制程式對資源的申請和使用,控制此程式可訪問資源的範圍並限定此程式異常之後能夠影響的範圍。在Linux系統中,不同程序之間資料是不共享的;對於每個程序來說,他無法訪問其他程序中的資料,因此一個程序需要與另外一個程序通訊,需要某種系統機制才能完成。
1.2 使用者空間/核心空間
詳細解釋可以參考Kernel Space Definition: http://www.linfo.org/kernel_space.html
在Linux系統中,對於Kernel這麼一個高安全級別的東西,系統顯然是不容許其它的應用程式隨便訪問的,所以需要對Kernel提供一定的保護機制,這個保護機制用來告訴那些應用程式,你只可以訪問某些許可的資源,不許可的資源是拒絕被訪問的,於是就把Kernel和上層的應用程式抽像的隔離開,分別稱之為使用者空間和核心空間
雖然從邏輯上抽離出使用者空間和核心空間;但是不可避免的的是,總有那麼一些使用者空間需要訪問核心空間的資源;比如應用程式訪問檔案,網路是很常見的事情,怎麼辦呢?使用者空間訪問核心空間的唯一方式就是系統呼叫
1.3 核心模組/驅動
通過系統呼叫,使用者空間可以訪問核心空間,那麼如果一個使用者空間想與另外一個使用者空間進行通訊怎麼辦呢?很自然想到的是讓作業系統核心新增支援;傳統的Linux通訊機制,比如Socket,管道等都是核心支援的;但是Binder並不是Linux核心的一部分,Android系統通過新增一個核心模組執行在核心空間,使用者程序之間的通過這個模組作為橋樑,就可以完成通訊了。
在Android系統中,這個執行在核心空間的,負責各個使用者程序通過Binder通訊的核心模組叫做Binder驅動
1.4 圖解
圖解:
- 每一個程序包含使用者空間和核心空間
- 程序A和程序B的使用者空間是不共享的,但是他們的核心空間通過Binder驅動關聯,這樣程序A和程序B就可以通過Binder驅動進行通訊
- 使用者空間訪問核心空間的唯一方式就是系統呼叫,這樣保證了系統的安全性
2. 為什麼要使用Binder
前面說過,在傳統的Linux上,還是有很多選擇可以用來實現程序間通訊,比如Socket,管道,共享記憶體等方式。那麼Android為什麼不使用這些原有的技術,而是要使開發一種新的叫Binder的程序間通訊機制呢?
2.1 安全方面
傳統的程序通訊方式對於通訊雙方的身份並沒有做出嚴格的驗證,比如Socket通訊ip地址是客戶端手動填入,很容易進行偽造,而Binder機制從協議本身就支援對通訊雙方做身份校檢,因而大大提升了安全性。
2.2 效能方面(一次資料拷貝)
傳統的IPC ,例如Pipe和Socket,執行一次通訊需要兩次資料拷貝
而Binder僅需要一次記憶體拷貝就實現了程序間通訊從而提高了執行效率,那麼他是如何實現的呢?
- 在程序建立時會呼叫到binder_open函式,在該函式中會開啟binder裝置檔案(/dev/binder)然後呼叫mmap函式
- mmap函式屬於系統呼叫,mmap會在核心空間中分配一塊記憶體,之後將這塊記憶體對映到使用者空間,這樣就可以像操作使用者空間那樣操作核心空間,這是一次拷貝的基礎。
- 當資料從使用者空間拷貝到核心空間的時候,是直接從當前程序的使用者空間拷貝到目標程序的核心空間,這個過程是在請求端執行緒中處理的,操作物件是目標程序的核心空間。
- 而由於Binder核心空間的資料能直接對映到使用者空間,這裡就不在需要拷貝到使用者空間。這就是一次拷貝的原理
3. Binder的Client/Server通訊模型
在Android系統的Binder機制中,由4個元件Client、Server、Service Manager和Binder Driver組成。Binder就是一種把這四個元件粘合在一起的粘結劑了,其中,核心元件是Binder Driver,Service Manager提供了輔助管理的功能,Client和Server正是在Binder Driver和Service Manager提供的基礎設施上,進行Client-Server之間的通訊。Service Manager和Binder Driver已經在Android平臺中實現好,開發者只要按照規範實現自己的Client和Server元件就可以了。
- Client、Server和Service Manager實現在使用者空間中,Binder Driver實現在核心空間中
- 在啟動過程中Server程序會先註冊一些Service到ServiceManager中,所以Server程序是ServiceManager的客戶端,而ServiceManager就是服務端了。ServiceManager是一個守護程序,他是service的管理者,能夠管理註冊的Service並向Client提供查詢Service的介面。
- 如果某個Client程序要使用某個Service,必須到ServiceManager中獲取該Service的相關資訊,所以Client是ServiceManager的客戶端,另外Client根據得到的Service資訊與Service所在的Server程序建立通訊的通路,然後就可以直接與Service互動了,所以,Client是Server的客戶端。
- Binder Driver提供裝置檔案/dev/binder與使用者空間互動,Client、Server和Service Manager通過open和ioctl檔案操作函式與Binder驅動程式進行通訊
- Binder Driver和Service Manager在Android平臺中已經實現,開發者只需要在使用者空間實現自己的Client和Server
- 三者的互動都是基於Binder通訊的,所以通過任意兩者之間的關係都能夠揭示Binder的奧祕。
4. Binder的上層實現
先給一張Binder相關的類圖一瞰Binder全貌。
通常來說,介面是分析程式碼的入口,Android中’I’ 打頭的類統統是介面類,從上圖中我們可以看到BBinder和BpBinder都繼承自IBinder,所以我們先從IBinder開始下手。
// frameworks/native/libs/binder/include/binder/IBinder.h
class IBinder : public virtual RefBase
{
public:
...
virtual sp<IInterface> queryLocalInterface(const String16& descriptor); //返回一個IInterface物件
...
virtual const String16& getInterfaceDescriptor() const = 0;
virtual bool isBinderAlive() const = 0;
virtual status_t pingBinder() = 0;
virtual status_t dump(int fd, const Vector<String16>& args) = 0;
virtual status_t transact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0) = 0;
virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0) = 0;
virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = NULL) = 0;
...
virtual BBinder* localBinder(); //返回一個BBinder物件
virtual BpBinder* remoteBinder(); //返回一個BpBinder物件
};
有介面必然有實現,從圖中可以看出,BBinder和BpBinder都是IBinder的實現類,它們幹啥用的,有啥區別?有興趣同學可以去分別去讀讀他們的程式碼,分別在
Bpinder: frameworks/native/libs/binder/BpBinder.cpp
BBinder: frameworks/native/libs/binder/Binder.cpp
這裡我們簡單總結一下他們的區別:
介面 | BBinder | BpBinder |
---|---|---|
queryLocalInterface() | 沒有實現 ,IBinder 預設實現 return NULL; | 沒有實現 ,IBinder 預設實現 return NULL; |
getInterfaceDescriptor() | return sEmptyDescriptor; | (this)->transact(INTERFACE_TRANSACTION, send, &reply); … mDescriptorCache = res; |
isBinderAlive() | return true; | return mAlive != 0; |
pingBinder() | return NoError; | transact(PING_TRANSACTION, send, &reply); |
linkToDeath() | return INVALID_OPERATION; | self->requestDeathNotification(mHandle, this); |
unlinkToDeath() | return INVALID_OPERATION; | self->clearDeathNotification(mHandle, this); |
localBinder() | return this; | 沒有實現, IBinder預設實現 return NULL; |
remoteBinder() | 沒有實現,IBinder預設實現 return NULL; | return this |
transact() | err = onTransact(code, data, reply, flags); | IPCThreadState::self()->transact(mHandle, code, data, reply, flags); |
onTransact() | switch (code) { case INTERFACE_TRANSACTION: … default: … } |
沒有實現 |
它們的差異在於它們是通訊兩端的不同實現,BBinder是服務端,而BpBinder是客戶端。為什麼這麼說:
- pingBinder, BBinder直接返回OK,而BpBinder需要執行一個transact函式,直接返回的應該是服務端
- linkToDeath()是用來在服務掛的時候通知客戶端的,那服務端當然不需要自己監視自己咯,所以BBinder直接返回非法,而Bpbinder需要通過requestDeathNotification()要求某人完成這個事情
- 在Android中,remote一般代表某個遠端物件的本地代理,所以remoteBinder()在BBinder中沒有實現而在BpBinder中返回自身
所以結論是,BBinder代表著服務端,而BpBinder則是它在客戶端的代理
客戶端通過BpBinder的transact()發起請求,而伺服器端的BBinder在onTranscat()裡響應請求,並將結果返回。
4.1 Binder Server端的實現
我們先看一看Binder Server端的實現(以MediaPlayer為例):
這裡先給一張Binder Server端的類圖,後面在分析程式碼的時候可以結合這張圖去理解,這樣理解起來還比較輕鬆。
前面已經給出了IBinder的程式碼,我們這裡從BBinder開始分析
// frameworks/native/libs/binder/include/binder/Binder.h
class BBinder : public IBinder
{
public:
BBinder();
virtual const String16& getInterfaceDescriptor() const;
virtual bool isBinderAlive() const;
virtual status_t pingBinder();
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t transact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0);
virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = NULL);
virtual void attachObject( const void* objectID,
void* object,
void* cleanupCookie,
object_cleanup_func func);
virtual void* findObject(const void* objectID) const;
virtual void detachObject(const void* objectID);
virtual BBinder* localBinder();
protected:
virtual ~BBinder();
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
private:
BBinder(const BBinder& o);
BBinder& operator=(const BBinder& o);
class Extras;
std::atomic<Extras*> mExtras;
void* mReserved0;
};
BBinder類繼承了IBinder並從Server端的角度實現了其中大部分方法,但是並沒有實現queryLocalInterface()方法,那麼一定有一個BBinder的子類實現了這個方法接著看下面的程式碼
// frameworks/native/libs/binder/include/binder/IInterface.h
// 類BnInterface定義
template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
virtual const String16& getInterfaceDescriptor() const;
protected:
virtual IBinder* onAsBinder();
};
// 函式queryLocalInterface()的實現
template<typename INTERFACE>
inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
const String16& _descriptor)
{
if (_descriptor == INTERFACE::descriptor) return this;
return NULL;
}
可以看到queryLocalInterface()函式是在BBinder的子類BnInterface中實現的,該方法將自己強制轉換成 IInterface物件返回,這個IInterface又是什麼?顯然IInterface不存在於BBinder中,通過上面的程式碼我們可以看到類BnInterface不僅繼承了BBinder還繼承了一個模板類INTERFACE,接下來我們看看這個模板類INTERFACE是什麼
// frameworks/av/media/libmedia/include/media/IMediaPlayer.h
class BnMediaPlayer: public BnInterface<IMediaPlayer>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在我們這個例子中BnMediaPlayer繼承自BnInterface那麼這個INTERFACE模板類就是IMediaPlayer,我們繼續跟進
// frameworks/av/media/libmedia/include/media/IMediaPlayer.h
class IMediaPlayer: public IInterface
{
public:
...
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
virtual status_t setDataSource(const sp<IStreamSource>& source) = 0;
virtual status_t setDataSource(const sp<IDataSource>& source) = 0;
virtual status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) = 0;
virtual status_t getBufferingSettings(
BufferingSettings* buffering /* nonnull */) = 0;
virtual status_t setBufferingSettings(const BufferingSettings& buffering) = 0;
virtual status_t prepareAsync() = 0;
virtual status_t start() = 0;
virtual status_t stop() = 0;
...
}
找到了!IMediaPlayer繼承了IInterface,IMediaPlayer中定義了很多介面,這些介面都是MediaServer向Client端所提供的。
// frameworks/native/libs/binder/include/binder/IInterface.h
class IInterface : public virtual RefBase
{
public:
IInterface();
static sp<IBinder> asBinder(const IInterface*);
static sp<IBinder> asBinder(const sp<IInterface>&);
protected:
virtual ~IInterface();
virtual IBinder* onAsBinder() = 0;
};
IInterface中定義了從Interface 到IBinder的轉換介面asBinder(), 和剛才我們研究的queryLocalInterface() 正好反過來,說明IBinder 和 IInterface 之間是可以互轉的。一個物件怎麼可以變成另外一個物件呢?唯一的解釋就是這個物件具有雙重身份,要麼他同時繼承 IInterface 和 IBinder, 要麼他體內有這兩個物件同時存在。我們回頭看BnMediaPlayer類
// frameworks/av/media/libmedia/include/media/IMediaPlayer.h
class BnMediaPlayer: public BnInterface<IMediaPlayer>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
class IMediaPlayer: public IInterface
{
public:
...
}
可以看出BnMediaPlayer既是IBinder的子類,也是IInterface的子類。這便是IBinder和IInterface可以相互轉換的原因。
到這裡Binder Server端的實現已經介紹完了,讀者可以結合前面的類圖再回顧一遍感受一下。
4.2 Binder Client端的實現
Server端的實現介紹完了接下來介紹Client端的實現,在Client端我們需要找到一個類,它必須同時擁有IBinder 和 IIterface的特性。
IIterface的子類IMediaPlayer中定義了Server端為Client端提供的方法,我們可以理解為通訊協議。而IBinder提供了兩者之間通訊的通路,要想實現通訊兩者都是必須的。
接下來先看BpBinder
// frameworks/native/libs/binder/include/binder/BpBinder.h
class BpBinder : public IBinder
跟IInterface 沒有關係,那麼一定是別人,看看BpInterface
// frameworks/native/libs/binder/include/binder/IInterface.h
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
BpInterface(const sp<IBinder>& remote);
protected:
virtual IBinder* onAsBinder();
};
這裡的INTERFACE 是 IMediaPlayer, 它繼承了IInterface, IInterface 的物件找到了, BpBinder和IInterface我們都找到了但是他們之間的關係是什麼呢?接下來我們看BpRefBase ,
// frameworks/native/libs/binder/include/binder/Binder.h
class BpRefBase : public virtual RefBase
{
protected:
...
inline IBinder* remote() { return mRemote; }
...
private:
...
IBinder* const mRemote;
RefBase::weakref_type* mRefs;
volatile int32_t mState;
};
有了,BpRefBase 裡有IBinder 成員變數mRemote,看來在客戶端,沒有一個類同時繼承IBinder 和 IInterface, 但是有一個類繼承了其一,但包含了另外一個,這種在設計模式裡稱為組合(Composition).
還是不太明白?還是用圖解釋吧。
看明白了?從BpInterface開始,通過BpRefBase 我們可以找到IBinder物件mRemote, 這個轉換就在 asBinder() 的實現裡,看看程式碼
// frameworks/native/libs/binder/include/binder/IInterface.h
sp<IBinder> IInterface::asBinder(){
return this ? onAsBinder() : NULL;
}
sp<const IBinder> IInterface::asBinder() const{
return this ? const_cast<IInterface*>(this)->onAsBinder() : NULL;
}
template<typename INTERFACE>
inline IBinder* BpInterface<INTERFACE>::onAsBinder()
{
return remote();
}
template<typename INTERFACE>
IBinder* BnInterface<INTERFACE>::onAsBinder()
{
return this;
}
這裡印證我們上面兩張圖的正確性,onAsBinder是轉換的發生的地方,服務端(BnInterface)的實現直接返回了自己,因為它繼承了兩者,而客戶端BpInterface則需要通過remote()函式獲取mRemote 成員變數,因為他自己本身不是IBinder。
接下來我們看看BpRefbase類中的mRemote是如何被賦值的?看看以下程式碼
//frameworks/native/libs/binder/Binder.cpp
BpRefBase::BpRefBase(const sp<IBinder>& o)
: mRemote(o.get()), mRefs(NULL), mState(0)
{
...
}
// frameworks/native/libs/binder/include/binder/IInterface.h
template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
: BpRefBase(remote)
{
}
// frameworks/av/media/libmedia/IMediaPlayer.cpp
class BpMediaPlayer: public BpInterface<IMediaPlayer>
{
public:
BpMediaPlayer(const sp<IBinder>& impl)
: BpInterface<IMediaPlayer>(impl)
{
}
...
}
結合類圖和上面的程式碼我們可以得出結論:BpRefBase內的mRemote物件是從子類BpMediaPlayer一級一級傳上去的。那唯一的問題就是在哪裡完成的這個注入操作,搜尋"new BpMediaPlayer",程式碼裡沒有,試試搜尋"IMediaPlayer“,發現了一點線索
// frameworks/av/media/libmedia/IMediaPlayerService.cpp
virtual sp<IMediaPlayer> create(
const sp<IMediaPlayerClient>& client, int audioSessionId) {
Parcel data, reply;
...
// 和Binder驅動進行互動,這部分內容在Android跨程序通訊Binder原理分析(二)中分析
remote()->transact(CREATE, data, &reply);
return interface_cast<IMediaPlayer>(reply.readStrongBinder()); //從reply裡讀出IBinder,然後轉成IMediaPlayer介面物件
}
這裡通過interface_cast 直接把IBinder 轉換成了 IMediaPlayer, interface_cast 又是什麼,他是怎麼轉換的?
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
繼續跟進 asInterface()函式, 結果發現裡以下程式碼
// frameworks/native/libs/binder/include/binder/IInterface.h
#define DECLARE_META_INTERFACE(INTERFACE) \
static const android::String16 descriptor; \
static android::sp<I##INTERFACE> asInterface( \
const android::sp<android::IBinder>& obj); \
virtual const android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE(); \
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
const android::String16 I##INTERFACE::descriptor(NAME); \
const android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
const android::sp<android::IBinder>& obj) \
{ \
android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
I##INTERFACE::descriptor).get()); \
if (intr == NULL) { \
intr = new Bp##INTERFACE(obj); \
} \
} \
return intr; \
}
恍然大悟,原來在DECLARE_META_INTERFACE 這個巨集裡定義了asInterface, 在IMPLEMENT_META_INTERFACE 裡實現了它,這裡果然有一個new BpMediaPlayer! BpRefBase內的mRemote物件是從子類BpMediaPlayer一級一級傳上去的,就是從這裡開始的。
這裡總結一下:
- Client端通過Binder驅動獲取一個IBinder物件並通過interface_cast轉成IMediaPlayer介面物件,IMediaPlayer就是使用者程式看到的物件,客戶端通過其呼叫IMediaPlayer的介面方法,最終調到BpBinder的transact(),進而呼叫到BBinder的onTransact();
- 在interface_cast中生成BpMediaPlayer,由於BpMediaPlayer是BpRefBase的子類,所以在構造的時候將IBinder物件一級一級的傳到BpRefBase並儲存在成員變數mRemote中
- 這樣BpInterface不僅是IInterface的子類,還擁有IBinder的成員變數,所以BpInterface同時擁有IBinder 和 IIterface的特性
為什麼要把Binder搞得那麼複雜?Google這麼做是希望通過這些封裝儘可能減少開發者的工作量,這樣在開發一個Native的Service 的時候,開發者只需要做這麼幾件事:
- 定義一個介面檔案, IXXXService, 繼承IInterface
- 定義BnXXXService(),繼承 BnInterface<IXXXService>
- 定義BpXXXService(),繼承BpInterface<IXXXService>
- 在BnXXXService中實現XXXService的服務端。
- 在BpXXXService中實現XXXService的代理端。
(其實從Android7.0開始,也可以通過aidl的方式實現Native的Service,這種方式簡化了Server的開發流程,有興趣的可以瞭解一下)
而在Client端使用Service的時候,讓訪問遠端服務就像呼叫本地函式一樣,Client端只需要通過ServiceManager獲取遠端服務的代理(建立物件),然後通過這個代理呼叫遠端服務的函式即可(呼叫物件中的方法)。