在Android系統(tǒng)中,針對移動設(shè)備內(nèi)存空間有限的特點,提供了一種在進(jìn)程間共享數(shù)據(jù)的機制:匿名共享內(nèi)存,它能夠輔助內(nèi)存管理系統(tǒng)來有效地管理內(nèi)存,它的實現(xiàn)原理我們在前面已經(jīng)分析過了。為了方便使用匿名共享內(nèi)存機制,系統(tǒng)還提供了Java調(diào)用接口(MemoryFile)和C++調(diào)用接口(MemoryHeapBase、MemoryBase),Java接口在前面也已經(jīng)分析過了,本文中將繼續(xù)分析它的C++接口。
在前面一篇文章Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)驅(qū)動程序源代碼分析中,我們分析了匿名共享內(nèi)存驅(qū)動程序Ashmem的實現(xiàn),重點介紹了它是如何輔助內(nèi)存管理系統(tǒng)來有效地管理內(nèi)存的,簡單來說,它就是給使用者提供鎖機制來輔助管理內(nèi)存,當(dāng)我們申請了一大塊匿名共享內(nèi)存時,中間過程有一部分不需要使用時,我們就可以將這一部分內(nèi)存塊解鎖,這樣內(nèi)存管理系統(tǒng)就可以把它回收回去了。接著又在前面一篇文章Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)在進(jìn)程間共享的原理分析中,我們分析了匿名共享內(nèi)存是如何通過Binder進(jìn)程間通信機制來實現(xiàn)在進(jìn)程間共享的,簡單來說,就是每一個匿名共享內(nèi)存塊都是一個文件,當(dāng)我們需要在進(jìn)程間共享時,就把這個文件的打開描述符通過Binder進(jìn)程間通信機制傳遞給另一外進(jìn)程,在傳遞的過程中,Binder驅(qū)動程序就通過這個復(fù)制這個打開文件描述符到目標(biāo)進(jìn)程中去,從而實現(xiàn)數(shù)據(jù)共享。在文章Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)簡要介紹和學(xué)習(xí)計劃中,我們介紹了如何在Android應(yīng)用程序中使用匿名共享內(nèi)存,主要是通過應(yīng)用程序框架層提供的MemoryFile接口來使用的,而MemoryFile接口是通過JNI方法調(diào)用到系統(tǒng)運行時庫層中的匿名共享內(nèi)存C接口,最終通過這些C接口來使用內(nèi)核空間中的匿名共享內(nèi)存驅(qū)動模塊。為了方便開發(fā)者靈活地使用匿名共享內(nèi)存,Android系統(tǒng)在應(yīng)用程序框架層中還提供了使用匿名共享內(nèi)存的C++接口,例如,Android應(yīng)用程序四大組件之一Content Provider,它在應(yīng)用程序間共享數(shù)據(jù)時,就是通過匿名共享內(nèi)存機制來實現(xiàn),但是它并不是通過MemoryFile接口來使用,而是通過調(diào)用C++接口中的MemoryBase類和MemoryHeapBase類來使用。在接下來的內(nèi)容中,我們就詳細(xì)分析MemoryHeapBase類和MemoryBase類的實現(xiàn),以及它們是如何實現(xiàn)在進(jìn)程間共享數(shù)據(jù)的。
如果我們想在進(jìn)程間共享一個完整的匿名共享內(nèi)存塊,可以通過使用MemoryHeapBase接口來實現(xiàn),如果我們只想在進(jìn)程間共享一個匿名共享內(nèi)存塊中的其中一部分時,就可以通過MemoryBase接口來實現(xiàn)。MemoryBase接口是建立在MemoryHeapBase接口的基礎(chǔ)上面的,它們都可以作為一個Binder對象來在進(jìn)程間傳輸,因此,希望讀者在繼續(xù)閱讀本文之前,對Android系統(tǒng)的Binder進(jìn)程間通信機制有一定的了解,具體可以參考前面一篇文章Android進(jìn)程間通信(IPC)機制Binder簡要介紹和學(xué)習(xí)計劃。下面我們就首先分析MemoryHeapBase接口的實現(xiàn),然后再分析MemoryBase接口的實現(xiàn),最后,通過一個實例來說明它們是如何使用的。
1. MemoryHeapBase
前面說到,MemoryHeapBase類的對象可以作為Binder對象在進(jìn)程間傳輸,作為一個Binder對象,就有Server端對象和Client端引用的概念,其中,Server端對象必須要實現(xiàn)一個BnInterface接口,而Client端引用必須要實現(xiàn)一個BpInterface接口。下面我們就先看一下MemoryHeapBase在Server端實現(xiàn)的類圖:
這個類圖中的類可以劃分為兩部分,一部分是和業(yè)務(wù)相關(guān)的,即跟匿名共享內(nèi)存操作相關(guān)的類,包括MemoryHeapBase、IMemoryBase和RefBase三個類,另一部分是和Binder機制相關(guān)的,包括IInterface、BnInterface、BnMemoryHeap、IBinder、BBinder、ProcessState和IPCThreadState七個類。
我們先來看跟匿名共享內(nèi)存業(yè)務(wù)相關(guān)的這部分類的邏輯關(guān)系。IMemoryBase定義了匿名共享內(nèi)操作的接口,而MemoryHeapBase是作為Binder機制中的Server角色的,因此,它需要實現(xiàn)IMemoryBase接口,此外,MemoryHeapBase還繼承了RefBase類。從前面一篇文章Android系統(tǒng)的智能指針(輕量級指針、強指針和弱指針)的實現(xiàn)原理分析中,我們知道,繼承了RefBase類的子類,它們的對象都可以結(jié)合Android系統(tǒng)的智能指針來使用,因此,我們在實例化MemoryHeapBase類時,可以通過智能指針來管理它們的生命周期。
再來看和Binder機制相關(guān)的這部分類的邏輯關(guān)系。從Android系統(tǒng)進(jìn)程間通信(IPC)機制Binder中的Server啟動過程源代碼分析這篇文章中,我們知道,所有的Binder對象都必須實現(xiàn)IInterface接口,無論是Server端實體對象,還是Client端引用對象,通過這個接口的asBinder成員函數(shù)我們可以獲得Binder對象的IBinder接口,然后通過Binder驅(qū)動程序把它傳輸給另外一個進(jìn)程。當(dāng)一個類的對象作為Server端的實體對象時,它還必須實現(xiàn)一個模板類BnInterface,這里負(fù)責(zé)實例化模板類BnInterface的類便是BnMemoryHeap類了,它里面有一個重要的成員函數(shù)onTransact,當(dāng)Client端引用請求Server端對象執(zhí)行命令時,Binder系統(tǒng)就會調(diào)用BnMemoryHeap類的onTransact成員函數(shù)來執(zhí)行具體的命令。當(dāng)一個類的對象作為Server端的實體對象時,它還要繼承于BBinder類,這是一個實現(xiàn)了IBinder接口的類,它里面有一個重要的成員函數(shù)transact,當(dāng)我們從Server端線程中接收到Client端的請求時,就會調(diào)用注冊在這個線程中的BBinder對象的transact函數(shù)來處理這個請求,而這個transact函數(shù)會將這些Client端請求轉(zhuǎn)發(fā)給BnMemoryHeap類的onTransact成員函數(shù)來處理。最后,ProcessState和IPCThreadState兩個類是負(fù)責(zé)和Binder驅(qū)動程序打交道的,其中,ProcessState負(fù)責(zé)打開Binder設(shè)備文件/dev/binder,打開了這個Binder設(shè)備文件后,就會得到一個打開設(shè)備文件描述符,而IPCThreadState就是通過這個設(shè)備文件描述符來和Binder驅(qū)動程序進(jìn)行交互的,例如它通過一個for循環(huán)來不斷地等待Binder驅(qū)動程序通知它有新的Client端請求到來了,一旦有新的Client端請求到來,它就會調(diào)用相應(yīng)的BBinder對象的transact函數(shù)來處理。
本文我們主要是要關(guān)注和匿名共享內(nèi)存業(yè)務(wù)相關(guān)的這部分類,即IMemoryBase和MemoryHeapBase類的實現(xiàn),和Binder機制相關(guān)的這部分類的實現(xiàn),可以參考Android進(jìn)程間通信(IPC)機制Binder簡要介紹和學(xué)習(xí)計劃一文。
IMemoryBase類主要定義了幾個重要的操作匿名共享內(nèi)存的方法,它定義在frameworks/base/include/binder/IMemory.h文件中:
class IMemoryHeap : public IInterface { public: ...... virtual int getHeapID() const = 0; virtual void* getBase() const = 0; virtual size_t getSize() const = 0; ...... };
成員函數(shù)getHeapID是用來獲得匿名共享內(nèi)存塊的打開文件描述符的;成員函數(shù)getBase是用來獲得匿名共享內(nèi)存塊的基地址的,有了這個地址之后,我們就可以在程序里面直接訪問這塊共享內(nèi)存了;成員函數(shù)getSize是用來獲得匿名共享內(nèi)存塊的大小的。
MemoryHeapBase類主要用來實現(xiàn)上面IMemoryBase類中列出來的幾個成員函數(shù)的,這個類聲明在frameworks/base/include/binder/MemoryHeapBase.h文件中:
class MemoryHeapBase : public virtual BnMemoryHeap { public: ...... /* * maps memory from ashmem, with the given name for debugging */ MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); ...... /* implement IMemoryHeap interface */ virtual int getHeapID() const; virtual void* getBase() const; virtual size_t getSize() const; ...... private: int mFD; size_t mSize; void* mBase; ...... }MemoryHeapBase類的實現(xiàn)定義在frameworks/base/libs/binder/MemoryHeapBase.cpp文件中,我們先來看一下它的構(gòu)造函數(shù)的實現(xiàn):
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(0), mNeedUnmap(false) { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); if (fd >= 0) { if (mapfd(fd, size) == NO_ERROR) { if (flags & READ_ONLY) { ashmem_set_prot_region(fd, PROT_READ); } } } }這個構(gòu)造函數(shù)有三個參數(shù),其中size表示要創(chuàng)建的匿名共享內(nèi)存的大小,flags是用來設(shè)置這塊匿名共享內(nèi)存的屬性的,例如是可讀寫的還是只讀的,name是用來標(biāo)識這個匿名共享內(nèi)存的名字的,可以傳空值進(jìn)來,這個參數(shù)只是作為調(diào)試信息使用的。
MemoryHeapBase類創(chuàng)建的匿名共享內(nèi)存是以頁為單位的,頁的大小一般為4K,但是是可以設(shè)置的,這個函數(shù)首先通過getpagesize函數(shù)獲得系統(tǒng)中一頁內(nèi)存的大小值,然后把size參數(shù)對齊到頁大小去,即如果size不是頁大小的整數(shù)倍時,就增加它的大小,使得它的值為頁大小的整數(shù)倍:
const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1));調(diào)整好size的大小后,就調(diào)用系統(tǒng)運行時庫層的C接口ashmem_create_region來創(chuàng)建一塊共享內(nèi)存了:
int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);這個函數(shù)我們在前面一篇文章Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)驅(qū)動程序源代碼分析中可以介紹過了,這里不再詳細(xì),它只要就是通過Ashmem驅(qū)動程序來創(chuàng)建一個匿名共享內(nèi)存文件,因此,它的返回值是一個文件描述符。
得到了這個匿名共享內(nèi)存的文件描述符后,還需要調(diào)用mapfd成函數(shù)把它映射到進(jìn)程地址空間去:
status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) { ...... if ((mFlags & DONT_MAP_LOCALLY) == 0) { void* base = (uint8_t*)mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); ...... mBase = base; ...... } else { ...... } mFD = fd; mSize = size; return NO_ERROR; }一般我們創(chuàng)建MemoryHeapBase類的實例時,都是需要把匿名共享內(nèi)存映射到本進(jìn)程的地址空間去的,因此,這里的條件(mFlags & DONT_MAP_LOCALLY == 0)為true,于是執(zhí)行系統(tǒng)調(diào)用mmap來執(zhí)行內(nèi)存映射的操作。
void* base = (uint8_t*)mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);傳進(jìn)去的第一個參數(shù)0表示由內(nèi)核來決定這個匿名共享內(nèi)存文件在進(jìn)程地址空間的起始位置,第二個參數(shù)size表示要映射的匿名共享內(nèi)文件的大小,第三個參數(shù)PROT_READ|PROT_WRITE表示這個匿名共享內(nèi)存是可讀寫的,第四個參數(shù)fd指定要映射的匿名共享內(nèi)存的文件描述符,第五個參數(shù)offset表示要從這個文件的哪個偏移位置開始映射。調(diào)用了這個函數(shù)之后,最后會進(jìn)入到內(nèi)核空間的ashmem驅(qū)動程序模塊中去執(zhí)行ashmem_map函數(shù),這個函數(shù)的實現(xiàn)具體可以參考Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)驅(qū)動程序源代碼分析一文,這里就不同詳細(xì)描述了。調(diào)用mmap函數(shù)返回之后,就得這塊匿名共享內(nèi)存在本進(jìn)程地址空間中的起始訪問地址了,將這個地址保存在成員變量mBase中,最后,還將這個匿名共享內(nèi)存的文件描述符和以及大小分別保存在成員變量mFD和mSize中。
回到前面MemoryHeapBase類的構(gòu)造函數(shù)中,將匿名共享內(nèi)存映射到本進(jìn)程的地址空間去后,還看繼續(xù)設(shè)置這塊匿名共享內(nèi)存的讀寫屬性:
if (fd >= 0) { if (mapfd(fd, size) == NO_ERROR) { if (flags & READ_ONLY) { ashmem_set_prot_region(fd, PROT_READ); } } }上面調(diào)用mapfd函數(shù)來映射匿名共享內(nèi)存時,指定這塊內(nèi)存是可讀寫的,但是如果傳進(jìn)來的參數(shù)flags設(shè)置了只讀屬性,那么還需要調(diào)用系統(tǒng)運行時庫存層的ashmem_set_prot_region函數(shù)來設(shè)置這塊匿名共享內(nèi)存為只讀,這個函數(shù)定義在system/core/libcutils/ashmem-dev.c文件,有興趣的讀者可以自己去研究一下。
這樣,通過這個構(gòu)造函數(shù),一塊匿名共享內(nèi)存就建立好了,其余的三個成員函數(shù)getHeapID、getBase和getSize就簡單了:
int MemoryHeapBase::getHeapID() const { return mFD; } void* MemoryHeapBase::getBase() const { return mBase; } size_t MemoryHeapBase::getSize() const { return mSize; }接下來我們再來看一下MemoryHeapBase在Client端實現(xiàn)的類圖:
這個類圖中的類也是可以劃分為兩部分,一部分是和業(yè)務(wù)相關(guān)的,即跟匿名共享內(nèi)存操作相關(guān)的類,包括BpMemoryHeap、IMemoryBase和RefBase三個類,另一部分是和Binder機制相關(guān)的,包括IInterface、BpInterface、BpRefBase、IBinder、BpBinder、ProcessState和IPCThreadState七個類。
在和匿名共享內(nèi)存操作相關(guān)的類中,BpMemoryHeap類是前面分析的MemoryHeapBase類在Client端進(jìn)程的遠(yuǎn)接接口類,當(dāng)Client端進(jìn)程從Service Manager或者其它途徑獲得了一個MemoryHeapBase對象的引用之后,就會在本地創(chuàng)建一個BpMemoryHeap對象來代表這個引用。BpMemoryHeap類同樣是要實現(xiàn)IMemoryHeap接口,同時,它是從RefBase類繼承下來的,因此,它可以與智能指針來結(jié)合使用。
在和Binder機制相關(guān)的類中,和Server端實現(xiàn)不一樣的地方是,Client端不需要實現(xiàn)BnInterface和BBinder兩個類,但是需要實現(xiàn)BpInterface、BpRefBase和BpBinder三個類。BpInterface類繼承于BpRefBase類,而在BpRefBase類里面,有一個成員變量mRemote,它指向一個BpBinder對象,當(dāng)BpMemoryHeap類需要向Server端對象發(fā)出請求時,它就會通過這個BpBinder對象的transact函數(shù)來發(fā)出這個請求。這里的BpBinder對象是如何知道要向哪個Server對象發(fā)出請深圳市的呢?它里面有一個成員變量mHandle,它表示的是一個Server端Binder對象的引用值,BpBinder對象就是要通過這個引用值來把請求發(fā)送到相應(yīng)的Server端對象去的了,這個引用值與Server端Binder對象的對應(yīng)關(guān)系是在Binder驅(qū)動程序內(nèi)部維護(hù)的。這里的ProcessSate類和IPCThreadState類的作用和在Server端的作用是類似的,它們都是負(fù)責(zé)和底層的Binder驅(qū)動程序進(jìn)行交互,例如,BpBinder對象的transact函數(shù)就通過線程中的IPCThreadState對象來將Client端請求發(fā)送出去的。這些實現(xiàn)具體可以參考Android系統(tǒng)進(jìn)程間通信(IPC)機制Binder中的Client獲得Server遠(yuǎn)程接口過程源代碼分析一文。
這里我們主要關(guān)注BpMemoryHeap類是如何實現(xiàn)IMemoryHeap接口的,這個類聲明和定義在frameworks/base/libs/binder/IMemory.cpp文件中:
class BpMemoryHeap : public BpInterface<IMemoryHeap> { public: BpMemoryHeap(const sp<IBinder>& impl); ...... virtual int getHeapID() const; virtual void* getBase() const; virtual size_t getSize() const; ...... private: mutable volatile int32_t mHeapId; mutable void* mBase; mutable size_t mSize; ...... }先來看構(gòu)造函數(shù)BpMemoryHeap的實現(xiàn):
BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl) : BpInterface<IMemoryHeap>(impl), mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false) { }它的實現(xiàn)很簡單,只是初始化一下各個成員變量,例如,表示匿名共享內(nèi)存文件描述符的mHeapId值初化為-1、表示匿名內(nèi)共享內(nèi)存基地址的mBase值初始化為MAP_FAILED以及表示匿名共享內(nèi)存大小的mSize初始為為0,它們都表示在Client端進(jìn)程中,這個匿名共享內(nèi)存還未準(zhǔn)備就緒,要等到第一次使用時才會去創(chuàng)建。這里還需要注意的一點,參數(shù)impl指向的是一個BpBinder對象,它里面包含了一個指向Server端Binder對象,即MemoryHeapBase對象的引用。
其余三個成員函數(shù)getHeapID、getBase和getSize的實現(xiàn)是類似的:
int BpMemoryHeap::getHeapID() const { assertMapped(); return mHeapId; } void* BpMemoryHeap::getBase() const { assertMapped(); return mBase; } size_t BpMemoryHeap::getSize() const { assertMapped(); return mSize; }即它們在使用之前,都會首先調(diào)用assertMapped函數(shù)來保證在Client端的匿名共享內(nèi)存是已經(jīng)準(zhǔn)備就緒了的:
void BpMemoryHeap::assertMapped() const { if (mHeapId == -1) { sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder()); sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get())); heap->assertReallyMapped(); if (heap->mBase != MAP_FAILED) { Mutex::Autolock _l(mLock); if (mHeapId == -1) { mBase = heap->mBase; mSize = heap->mSize; android_atomic_write( dup( heap->mHeapId ), &mHeapId ); } } else { // something went wrong free_heap(binder); } } }在解釋這個函數(shù)之前,我們需要先了解一下BpMemoryHeap是如何知道自己內(nèi)部維護(hù)的這塊匿名共享內(nèi)存有沒有準(zhǔn)備就緒的。
在frameworks/base/libs/binder/IMemory.cpp文件中,定義了一個全局變量gHeapCache:
static sp<HeapCache> gHeapCache = new HeapCache();
它的類型為HeapCache,這也是一個定義在frameworks/base/libs/binder/IMemory.cpp文件的類,它里面維護(hù)了本進(jìn)程中所有的MemoryHeapBase對象的引用。由于在Client端進(jìn)程中,可能會有多個引用,即多個BpMemoryHeap對象,對應(yīng)同一個MemoryHeapBase對象(這是由于可以用同一個BpBinder對象來創(chuàng)建多個BpMemoryHeap對象),因此,當(dāng)?shù)谝粋€BpMemoryHeap對象在本進(jìn)程中映射好這塊匿名共享內(nèi)存之后,后面的BpMemoryHeap對象就可以直接使用了,不需要再映射一次,當(dāng)然重新再映射一次沒有害處,但是會是多此一舉,Google在設(shè)計這個類時,可以說是考慮得非常周到的。
我們來看一下HeapCache的實現(xiàn):
class HeapCache : public IBinder::DeathRecipient { public: HeapCache(); virtual ~HeapCache(); ...... sp<IMemoryHeap> find_heap(const sp<IBinder>& binder); void free_heap(const sp<IBinder>& binder); sp<IMemoryHeap> get_heap(const sp<IBinder>& binder); ...... private: // For IMemory.cpp struct heap_info_t { sp<IMemoryHeap> heap; int32_t count; }; ...... Mutex mHeapCacheLock; KeyedVector< wp<IBinder>, heap_info_t > mHeapCache; };它里面定義了一個成員變量mHeapCache,用來維護(hù)本進(jìn)程中的所有BpMemoryHeap對象,同時還提供了find_heap和get_heap函數(shù)來查找內(nèi)部所維護(hù)的BpMemoryHeap對象的功能。函數(shù)find_heap和get_heap的區(qū)別是,在find_heap函數(shù)中,如果在mHeapCache找不到相應(yīng)的BpMemoryHeap對象,就會把這個BpMemoryHeap對象加入到mHeapCache中去,而在get_heap函數(shù)中,則不會自動把這個BpMemoryHeap對象加入到mHeapCache中去。
這里,我們主要看一下find_heap函數(shù)的實現(xiàn):
sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder) { Mutex::Autolock _l(mHeapCacheLock); ssize_t i = mHeapCache.indexOfKey(binder); if (i>=0) { heap_info_t& info = mHeapCache.editValueAt(i); LOGD_IF(VERBOSE, "found binder=%p, heap=%p, size=%d, fd=%d, count=%d", binder.get(), info.heap.get(), static_cast<BpMemoryHeap*>(info.heap.get())->mSize, static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId, info.count); android_atomic_inc(&info.count); return info.heap; } else { heap_info_t info; info.heap = interface_cast<IMemoryHeap>(binder); info.count = 1; //LOGD("adding binder=%p, heap=%p, count=%d", // binder.get(), info.heap.get(), info.count); mHeapCache.add(binder, info); return info.heap; } }這個函數(shù)很簡單,首先它以傳進(jìn)來的參數(shù)binder為關(guān)鍵字,在mHeapCache中查找,看看是否有對應(yīng)的heap_info對象info存在,如果有的話,就增加它的引用計數(shù)info.count值,表示這個BpBinder對象多了一個使用者;如果沒有的話,那么就需要創(chuàng)建一個heap_info對象info,并且將它加放到mHeapCache中去了。
回到前面BpMemoryHeap類中的assertMapped函數(shù)中,如果本BpMemoryHeap對象中的mHeapID等于-1,那么就說明這個BpMemoryHeap對象中的匿名共享內(nèi)存還沒準(zhǔn)備就緒,因此,需要執(zhí)行一次映射匿名共享內(nèi)存的操作。
在執(zhí)行映射操作之作,先要看看在本進(jìn)程中是否有其它映射到同一個MemoryHeapBase對象的BpMemoryHeap對象存在:
sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder()); sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));這里的find_heap函數(shù)是BpMemoryHeap的成員函數(shù),最終它調(diào)用了前面提到的全局變量gHeapCache來直正執(zhí)行查找的操作:
class BpMemoryHeap : public BpInterface<IMemoryHeap> { ...... private: static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) { return gHeapCache->find_heap(binder); } ...... }注意,這里通過find_heap函數(shù)得到BpMemoryHeap對象可能是和正在執(zhí)行assertMapped函數(shù)中的BpMemoryHeap對象一樣,也可能不一樣,但是這沒有關(guān)系,這兩種情況的處理方式都是一樣的,都是通過調(diào)用這個通過find_heap函數(shù)得到BpMemoryHeap對象的assertReallyMapped函數(shù)來進(jìn)一步確認(rèn)它內(nèi)部的匿名共享內(nèi)存是否已經(jīng)映射到進(jìn)程空間了:
void BpMemoryHeap::assertReallyMapped() const { if (mHeapId == -1) { // remote call without mLock held, worse case scenario, we end up // calling transact() from multiple threads, but that's not a problem, // only mmap below must be in the critical section. Parcel data, reply; data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); status_t err = remote()->transact(HEAP_ID, data, &reply); int parcel_fd = reply.readFileDescriptor(); ssize_t size = reply.readInt32(); uint32_t flags = reply.readInt32(); LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%ld, err=%d (%s)", asBinder().get(), parcel_fd, size, err, strerror(-err)); int fd = dup( parcel_fd ); LOGE_IF(fd==-1, "cannot dup fd=%d, size=%ld, err=%d (%s)", parcel_fd, size, err, strerror(errno)); int access = PROT_READ; if (!(flags & READ_ONLY)) { access |= PROT_WRITE; } Mutex::Autolock _l(mLock); if (mHeapId == -1) { mRealHeap = true; mBase = mmap(0, size, access, MAP_SHARED, fd, 0); if (mBase == MAP_FAILED) { LOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)", asBinder().get(), size, fd, strerror(errno)); close(fd); } else { mSize = size; mFlags = flags; android_atomic_write(fd, &mHeapId); } } } }如果成員變量mHeapId的值為-1,就說明還沒有把在Server端的MemoryHeapBase對象中的匿名共享內(nèi)存映射到本進(jìn)程空間來,于是,就通過一個Binder進(jìn)程間調(diào)用把Server端的MemoryHeapBase對象中的匿名共享內(nèi)存對象信息取回來:
Parcel data, reply; data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); status_t err = remote()->transact(HEAP_ID, data, &reply); int parcel_fd = reply.readFileDescriptor(); ssize_t size = reply.readInt32(); uint32_t flags = reply.readInt32(); ...... int fd = dup( parcel_fd ); ......取回來的信息包括MemoryHeapBase對象中的匿名共享內(nèi)存在本進(jìn)程中的文件描述符fd、大小size以及訪問屬性flags。如何把MemoryHeapBase對象中的匿名共享內(nèi)存作為本進(jìn)程的一個打開文件描述符,請參考前面一篇文章Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)在進(jìn)程間共享的原理分析。有了這個文件描述符fd后,就可以對它進(jìn)行內(nèi)存映射操作了:
Mutex::Autolock _l(mLock); if (mHeapId == -1) { mRealHeap = true; mBase = mmap(0, size, access, MAP_SHARED, fd, 0); if (mBase == MAP_FAILED) { LOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)", asBinder().get(), size, fd, strerror(errno)); close(fd); } else { mSize = size; mFlags = flags; android_atomic_write(fd, &mHeapId); } }前面已經(jīng)判斷過mHeapId是否為-1了,這里為什么又要重新判斷一次呢?這里因為,在上面執(zhí)行Binder進(jìn)程間調(diào)用的過程中,很有可能也有其它的線程也對這個BpMemoryHeap對象執(zhí)行匿名共享內(nèi)存映射的操作,因此,這里還要重新判斷一下mHeapId的值是否為-1,如果是的話,就要執(zhí)行匿名共享內(nèi)存映射的操作了,這是通過調(diào)用mmap函數(shù)來進(jìn)行的,這個函數(shù)我們前面在分析MemoryHeapBase類的實現(xiàn)時已經(jīng)見過了。
從assertReallyMapped函數(shù)返回到assertMapped函數(shù)中:
if (heap->mBase != MAP_FAILED) { Mutex::Autolock _l(mLock); if (mHeapId == -1) { mBase = heap->mBase; mSize = heap->mSize; android_atomic_write( dup( heap->mHeapId ), &mHeapId ); } } else { // something went wrong free_heap(binder); }如果heap->mBase的值不為MAP_FAILED,就說明這個heap對象中的匿名共享內(nèi)存已經(jīng)映射好了。進(jìn)入到里面的if語句,如果本BpMemoryHeap對象中的mHeap成員變量的值不等待-1,就說明前面通過find_heap函數(shù)得到的BpMemoryHeap對象和正在執(zhí)行assertMapped函數(shù)的BpMemoryHeap對象是同一個對象了,因此,什么也不用做就可以返回了,否則的話,就要初始化一下本BpMemoryHeap對象的相關(guān)成員變量了:
mBase = heap->mBase; mSize = heap->mSize; android_atomic_write( dup( heap->mHeapId ), &mHeapId );注意,由于這塊匿名共享內(nèi)存已經(jīng)在本進(jìn)程中映射好了,因此,這里不需要再執(zhí)行一次mmap操作,只需要把heap對象的相應(yīng)成員變量的值拷貝過來就行了,不過對于文件描述符,需要通過dup函數(shù)來復(fù)制一個。
這樣,BpMemoryHeap對象中的匿名共享內(nèi)存就準(zhǔn)備就緒了,可以通過使用的它mBase成員變量來直接訪問這塊匿名共享內(nèi)存。
至此,MemoryHeapBase類的實現(xiàn)就分析完了,下面我們繼續(xù)分析MemoryBase類的實現(xiàn)。
2. MemoryBase
文章開始時說過,MemoryBase接口是建立在MemoryHeapBase接口的基礎(chǔ)上的,它們都可以作為一個Binder對象來在進(jìn)程間進(jìn)行數(shù)據(jù)共享,它們的關(guān)系如下所示:
MemoryBase類包含了一個成員變量mHeap,它的類型的IMemoryHeap,MemoryBase類所代表的匿名共享內(nèi)存就是通過這個成員變量來實現(xiàn)的。
與MemoryHeapBase的分析過程一樣,我們先來看MemoryBase類在Server端的實現(xiàn),然后再來看它在Client端的實現(xiàn)。
MemoryBase在Server端實現(xiàn)的類圖如下所示:
MemoryBase類在Server端的實現(xiàn)與MemoryHeapBase類在Server端的實現(xiàn)是類似的,這里只要把IMemory類換成IMemoryHeap類、把BnMemory類換成BnMemoryHeap類以及MemoryBase類換成MemoryHeapBase類就變成是MemoryHeapBase類在Server端的實現(xiàn)了,因此,我們這里只簡單分析IMemory類和MemoryBase類的實現(xiàn)。
IMemory類定義了MemoryBase類所需要實現(xiàn)的接口,這個類定義在frameworks/base/include/binder/IMemory.h文件中:
class IMemory : public IInterface { public: ...... virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0; ...... void* pointer() const; size_t size() const; ssize_t offset() const; };成員函數(shù)getMemory用來獲取內(nèi)部的MemoryHeapBase對象的IMemoryHeap接口;成員函數(shù)pointer()用來獲取內(nèi)部所維護(hù)的匿名共享內(nèi)存的基地址;成員函數(shù)size()用來獲取內(nèi)部所維護(hù)的匿名共享內(nèi)存的大小;成員函數(shù)offset()用來獲取內(nèi)部所維護(hù)的這部分匿名共享內(nèi)存在整個匿名共享內(nèi)存中的偏移量。
IMemory類本身實現(xiàn)了pointer、size和offset三個成員函數(shù),因此,它的子類,即MemoryBase類,只需要實現(xiàn)getMemory成員函數(shù)就可以了。IMemory類的實現(xiàn)定義在frameworks/base/libs/binder/IMemory.cpp文件中:
void* IMemory::pointer() const { ssize_t offset; sp<IMemoryHeap> heap = getMemory(&offset); void* const base = heap!=0 ? heap->base() : MAP_FAILED; if (base == MAP_FAILED) return 0; return static_cast<char*>(base) + offset; } size_t IMemory::size() const { size_t size; getMemory(NULL, &size); return size; } ssize_t IMemory::offset() const { ssize_t offset; getMemory(&offset); return offset; }MemoryBase類聲明在frameworks/base/include/binder/MemoryBase.h文件中:
class MemoryBase : public BnMemory { public: MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size); ...... virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const; ...... private: size_t mSize; ssize_t mOffset; sp<IMemoryHeap> mHeap; };MemoryBase類實現(xiàn)在frameworks/base/libs/binder/MemoryBase.cpp文件中:
MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size) : mSize(size), mOffset(offset), mHeap(heap) { } sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const { if (offset) *offset = mOffset; if (size) *size = mSize; return mHeap; }在它的構(gòu)造函數(shù)中,接受三個參數(shù),參數(shù)heap指向的是一個MemoryHeapBase對象,真正的匿名共享內(nèi)存就是由它來維護(hù)的,參數(shù)offset表示這個MemoryBase對象所要維護(hù)的這部分匿名共享內(nèi)存在整個匿名共享內(nèi)存塊中的起始位置,參數(shù)size表示這個MemoryBase對象所要維護(hù)的這部分匿名共享內(nèi)存的大小。
成員函數(shù)getMemory的實現(xiàn)很簡單,只是簡單地返回內(nèi)部的MemoryHeapBase對象的IMemoryHeap接口,如果傳進(jìn)來的參數(shù)offset和size不為NULL,還會把其內(nèi)部維護(hù)的這部分匿名共享內(nèi)存在整個匿名共享內(nèi)存塊中的偏移位置以及這部分匿名共享內(nèi)存的大小返回給調(diào)用者。
這里我們可以看出,MemoryBase在Server端的實現(xiàn)只是簡單地封裝了MemoryHeapBase的實現(xiàn)。
下面我們再來看MemoryBase類在Client端的實現(xiàn),同樣,先看它們的類圖關(guān)系:
這個圖中我們可以看出,MemoryBase類在Client端的實現(xiàn)與MemoryHeapBase類在Client端的實現(xiàn)是類似的,這里只要把IMemory類換成IMemoryHeap類以及把BpMemory類換成BpMemoryHeap類就變成是MemoryHeapBase類在Client端的實現(xiàn)了,因此,我們這里只簡單分析BpMemory類的實現(xiàn),前面已經(jīng)分析過IMemory類的實現(xiàn)了。
BpMemory類實現(xiàn)在frameworks/base/libs/binder/IMemory.cpp文件中,我們先看它的聲明:
class BpMemory : public BpInterface<IMemory> { public: BpMemory(const sp<IBinder>& impl); virtual ~BpMemory(); virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const; private: mutable sp<IMemoryHeap> mHeap; mutable ssize_t mOffset; mutable size_t mSize; };和MemoryBase類一樣,它實現(xiàn)了IMemory類的getMemory成員函數(shù),在它的成員變量中,mHeap的類型為IMemoryHeap,它指向的是一個BpMemoryHeap對象,mOffset表示這個BpMemory對象所要維護(hù)的這部分匿名共享內(nèi)存在整個匿名共享內(nèi)存塊中的起始位置,mSize表示這個BpMemory對象所要維護(hù)的這部分匿名共享內(nèi)存的大小。
下面我們就看一下BpMemory類的成員函數(shù)getMemory的實現(xiàn):
sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const { if (mHeap == 0) { Parcel data, reply; data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { sp<IBinder> heap = reply.readStrongBinder(); ssize_t o = reply.readInt32(); size_t s = reply.readInt32(); if (heap != 0) { mHeap = interface_cast<IMemoryHeap>(heap); if (mHeap != 0) { mOffset = o; mSize = s; } } } } if (offset) *offset = mOffset; if (size) *size = mSize; return mHeap; }如果成員變量mHeap的值為NULL,就表示這個BpMemory對象尚未建立好匿名共享內(nèi)存,于是,就會通過一個Binder進(jìn)程間調(diào)用去Server端請求匿名共享內(nèi)存信息,在這些信息中,最重要的就是這個Server端的MemoryHeapBase對象的引用heap了,通過這個引用可以在Client端進(jìn)程中創(chuàng)建一個BpMemoryHeap遠(yuǎn)程接口,最后將這個BpMemoryHeap遠(yuǎn)程接口保存在成員變量mHeap中,同時,從Server端獲得的信息還包括這塊匿名共享內(nèi)存在整個匿名共享內(nèi)存中的偏移位置以及大小。這樣,這個BpMemory對象中的匿名共享內(nèi)存就準(zhǔn)備就緒了。
至此,MemoryBase類的實現(xiàn)就分析完了,下面我們將通過一個實例來說明如何使用MemoryBase類在進(jìn)程間進(jìn)行內(nèi)存共享,因為MemoryBase內(nèi)部使用了MemoryHeapBase類,所以,這個例子同時也可以說明MemoryHeapBase類的使用方法。
3. MemoryHeapBas類e和MemoryBase類的使用示例
在這個例子中,我們將在Android源代碼工程的external目錄中創(chuàng)建一個ashmem源代碼工程,它里面包括兩個應(yīng)用程序,一個是Server端應(yīng)用程序SharedBufferServer,它提供一段共享內(nèi)存來給Client端程序使用,一個是Client端應(yīng)用程序SharedBufferClient,它簡單地對Server端提供的共享內(nèi)存進(jìn)行讀和寫的操作。Server端應(yīng)用程序SharedBufferServer和Client端應(yīng)用程序SharedBufferClient通過Binder進(jìn)程間通信機制來交互,因此,我們需要定義自己的Binder對象接口ISharedBuffer。Server端應(yīng)用程序SharedBufferServer在內(nèi)部實現(xiàn)了一個服務(wù)SharedBufferService,這個服務(wù)托管給Service Manager來管理,因此,Client端應(yīng)用程序SharedBufferClient可以向Service Manager請求這個SharedBufferService服務(wù)的一個遠(yuǎn)接接口,然后就可以通過這個服務(wù)來操作Server端提供的這段共享內(nèi)存了。
這個工程由三個模塊組成,第一個模塊定義服務(wù)接口,它的相關(guān)源代碼位于external/ashmem/common目錄下,第二個模塊實現(xiàn)Server端應(yīng)用程序SharedBufferServer,它的相關(guān)源代碼位于external/ashmem/server目錄下,第三個模塊實現(xiàn)Client端應(yīng)用程序SharedBufferClient,它的相關(guān)源代碼碼位于external/ashmem/client目錄下。
首先來看common模塊中的服務(wù)接口的定義。在external/ashmem/common目錄下,有兩個源文件ISharedBuffer.h和ISharedBuffer.cpp。源文件ISharedBuffer.h定義了服務(wù)的接口:
#ifndef ISHAREDBUFFER_H_ #define ISHAREDBUFFER_H_ #include <utils/RefBase.h> #include <binder/IInterface.h> #include <binder/Parcel.h> #define SHARED_BUFFER_SERVICE "shy.luo.SharedBuffer" #define SHARED_BUFFER_SIZE 4 using namespace android; class ISharedBuffer: public IInterface { public: DECLARE_META_INTERFACE(SharedBuffer); virtual sp<IMemory> getBuffer() = 0; }; class BnSharedBuffer: public BnInterface<ISharedBuffer> { public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); }; #endif這個文件定義了一個ISharedBuffer接口,里面只有一個成員函數(shù)getBuffer,通過這個成員函數(shù),Client端可以從Server端獲得一個匿名共享內(nèi)存,這塊匿名共享內(nèi)存通過我們上面分析的MemoryBase類來維護(hù)。這個文件同時也定義了一個必須要在Server端實現(xiàn)的BnSharedBuffer接口,它里面只有一個成員函數(shù)onTransact,這個成員函數(shù)是用來處理Client端發(fā)送過來的請求的。除了定義這兩個接口之外,這個文件還定義了兩個公共信息,一個是定義常量SHARED_BUFFER_SERVICE,它是Server端提供的內(nèi)存共享服務(wù)的名稱,即這個內(nèi)存共享服務(wù)在Service Manager中是以SHARED_BUFFER_SERVICE來作關(guān)鍵字索引的,另外一個是定義常量SHARED_BUFFER_SIZE,它定義了Server端共享的內(nèi)存塊的大小,它的大小設(shè)置為4個字節(jié),在這個例子,將把這個共享內(nèi)存當(dāng)作一個整型變量來訪問。
源代文件ISharedBuffer.cpp文件定義了一個在Client端使用的BpSharedBuffer接口,它是指向運行在Server端的實現(xiàn)了ISharedBuffer接口的內(nèi)存共享服務(wù)的遠(yuǎn)程接口,同時,在這個文件里面,也實現(xiàn)了BnSharedBuffer類的onTransact成員函數(shù):
#define LOG_TAG "ISharedBuffer" #include <utils/Log.h> #include <binder/MemoryBase.h> #include "ISharedBuffer.h" using namespace android; enum { GET_BUFFER = IBinder::FIRST_CALL_TRANSACTION }; class BpSharedBuffer: public BpInterface<ISharedBuffer> { public: BpSharedBuffer(const sp<IBinder>& impl) : BpInterface<ISharedBuffer>(impl) { } public: sp<IMemory> getBuffer() { Parcel data; data.writeInterfaceToken(ISharedBuffer::getInterfaceDescriptor()); Parcel reply; remote()->transact(GET_BUFFER, data, &reply); sp<IMemory> buffer = interface_cast<IMemory>(reply.readStrongBinder()); return buffer; } }; IMPLEMENT_META_INTERFACE(SharedBuffer, "shy.luo.ISharedBuffer"); status_t BnSharedBuffer::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case GET_BUFFER: { CHECK_INTERFACE(ISharedBuffer, data, reply); sp<IMemory> buffer = getBuffer(); if(buffer != NULL) { reply->writeStrongBinder(buffer->asBinder()); } return NO_ERROR; } default: { return BBinder::onTransact(code, data, reply, flags); } } }在BpSharedBuffer類的成員函數(shù)transact中,向Server端發(fā)出了一個請求代碼為GET_BUFFER的Binder進(jìn)程間調(diào)用請求,請求Server端返回一個匿名共享內(nèi)存對象的遠(yuǎn)程接口IMemory,它實際指向的是一個BpMemory對象,獲得了這個對象之后,就將它返回給調(diào)用者;在BnSharedBuffer類的成員函數(shù)onTransact中,當(dāng)它接收到從Client端發(fā)送過來的代碼為GET_BUFFER的Binder進(jìn)程間調(diào)用請求后,便調(diào)用其子類的getBuffer成員函數(shù)來獲一個匿名共享內(nèi)存對象接口IMemory,它實際指向的是一個MemoryBase對象,獲得了這個對象之后,就把它返回給Client端。
接下來,我們再來看看server模塊的實現(xiàn)。在external/ashmem/common目錄下,只有一個源文件SharedBufferServer.cpp,它實現(xiàn)了內(nèi)存共享服務(wù)SharedBufferService:
#define LOG_TAG "SharedBufferServer" #include <utils/Log.h> #include <binder/MemoryBase.h> #include <binder/MemoryHeapBase.h> #include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> #include "../common/ISharedBuffer.h" class SharedBufferService : public BnSharedBuffer { public: SharedBufferService() { sp<MemoryHeapBase> heap = new MemoryHeapBase(SHARED_BUFFER_SIZE, 0, "SharedBuffer"); if(heap != NULL) { mMemory = new MemoryBase(heap, 0, SHARED_BUFFER_SIZE); int32_t* data = (int32_t*)mMemory->pointer(); if(data != NULL) { *data = 0; } } } virtual ~SharedBufferService() { mMemory = NULL; } public: static void instantiate() { defaultServiceManager()->addService(String16(SHARED_BUFFER_SERVICE), new SharedBufferService()); } virtual sp<IMemory> getBuffer() { return mMemory; } private: sp<MemoryBase> mMemory; }; int main(int argc, char** argv) { SharedBufferService::instantiate(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); return 0; }SharedBufferService服務(wù)實現(xiàn)了BnSharedBuffer接口。在它的構(gòu)造函數(shù)里面,首先是使用MemoryHeapBase類創(chuàng)建了一個匿名共享內(nèi)存,大小為SHARED_BUFFER_SIZE。接著,又以這個MemoryHeapBase對象為參數(shù),創(chuàng)建一個MemoryBase對象,這個MemoryBase對象指定要維護(hù)的匿名共享內(nèi)存的的偏移位置為0,大小為SHARED_BUFFER_SIZE,并且,將這個匿名共享內(nèi)存當(dāng)作一個整型變量地址,將它初始化為0。最終,這個匿名共享內(nèi)存對象保存在SharedBufferService類的成員變量mMemory中,這個匿名共享內(nèi)存對象可以通過成員函數(shù)getBuffer來獲得。
在Server端應(yīng)用程序的入口函數(shù)main中,首先是調(diào)用SharedBufferService靜態(tài)成員函數(shù)instantiate函數(shù)來創(chuàng)建一個SharedBufferService實例,然后通過defaultServiceManager函數(shù)來獲得系統(tǒng)中的Service Manager接口,最后通過這個Service Manager接口的addService函數(shù)來把這個SharedBufferService服務(wù)添加到Service Manager中去,這樣,Client端就可以通過Service Manager來獲得這個共享內(nèi)存服務(wù)了。有關(guān)Service Manager的實現(xiàn),請參考前面一篇文章淺談Service Manager成為Android進(jìn)程間通信(IPC)機制Binder守護(hù)進(jìn)程之路,而用來獲取Service Manager接口的defaultServiceManager函數(shù)的實現(xiàn)可以參考另外一篇文章淺談Android系統(tǒng)進(jìn)程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路。初始化好這個共享內(nèi)存服務(wù)之后,程序就通過ProcessState::self()->startThreadPool()函數(shù)來創(chuàng)建一個線程等待Client端來請求服務(wù)了,最后,程序的主線程也通過IPCThreadState::self()->joinThreadPool()函數(shù)來進(jìn)入到等待Client端來請求服務(wù)的狀態(tài)中。
我們還需要為這個Server端應(yīng)用程序編譯一個編譯腳本,在external/ashmem/server目錄下,新建一個Android.mk文件,它的內(nèi)容如下所示:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := ../common/ISharedBuffer.cpp \ SharedBufferServer.cpp LOCAL_SHARED_LIBRARIES:= libcutils libutils libbinder LOCAL_MODULE := SharedBufferServer include $(BUILD_EXECUTABLE)最后,我們再來看看client模塊的實現(xiàn)。在external/ashmem/client目錄下,只有一個源文件SharedBufferClient.cpp,它的內(nèi)容如下所示:
#define LOG_TAG "SharedBufferClient" #include <utils/Log.h> #include <binder/MemoryBase.h> #include <binder/IServiceManager.h> #include "../common/ISharedBuffer.h" int main() { sp<IBinder> binder = defaultServiceManager()->getService(String16(SHARED_BUFFER_SERVICE)); if(binder == NULL) { printf("Failed to get service: %s.\n", SHARED_BUFFER_SERVICE); return -1; } sp<ISharedBuffer> service = ISharedBuffer::asInterface(binder); if(service == NULL) { return -2; } sp<IMemory> buffer = service->getBuffer(); if(buffer == NULL) { return -3; } int32_t* data = (int32_t*)buffer->pointer(); if(data == NULL) { return -4; } printf("The value of the shared buffer is %d.\n", *data); *data = *data + 1; printf("Add value 1 to the shared buffer.\n"); return 0; }在這個文件中,主要就是定義了Client端應(yīng)用程序的入口函數(shù)main,在這個main函數(shù)里面,首先通過Service Manager接口獲得前面所實現(xiàn)的匿名共享內(nèi)存服務(wù)SharedBufferService的遠(yuǎn)程接口service,然后通過這個遠(yuǎn)程接口的getBuffer成員函數(shù)獲得由Server端提供的一塊匿名共享內(nèi)存接口buffer,最后通過這個匿名共享內(nèi)存接口獲得這個匿名共享內(nèi)存的基地址data。有了這個匿名共享內(nèi)存的地址data之后,我們就可以對它進(jìn)行讀寫了,先是把這個匿名共享內(nèi)存當(dāng)作是一個整型變量地址進(jìn)行訪問,并輸出它的值的大小,然后對這個整量變量進(jìn)行加1的操作,并寫回到原來的共享內(nèi)存空間中去。這樣,當(dāng)Server端應(yīng)用程序運行之后,第一次運行這個Client端應(yīng)用程序時,輸出的值為0,第二次運行這個個Client端應(yīng)用程序時,輸出的值為1,第三次運行這個個Client端應(yīng)用程序時,輸出的值為3......依次類推,后面我們將在模擬器中對這個分析進(jìn)行驗證,如果驗證成功的話,就說明這個匿名共享內(nèi)存成功地在Server端和Client端實現(xiàn)共享了。
同樣,我們需要為這個Client端應(yīng)用程序編譯一個編譯腳本,在external/ashmem/client目錄下,新建一個Android.mk文件,它的內(nèi)容如下所示:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := ../common/ISharedBuffer.cpp \ SharedBufferClient.cpp LOCAL_SHARED_LIBRARIES:= libcutils libutils libbinder LOCAL_MODULE := SharedBufferClient include $(BUILD_EXECUTABLE)源代碼都準(zhǔn)備好了之后,就可以對Server端和Client端應(yīng)用程序進(jìn)行編譯了。關(guān)于如何單獨編譯Android源代碼工程中的模塊,以及如何打包system.img,請參考如何單獨編譯Android源代碼中的模塊一文。
執(zhí)行以下命令進(jìn)行編譯和打包:
USER-NAME@MACHINE-NAME:~/Android$ mmm external/ashmem/server USER-NAME@MACHINE-NAME:~/Android$ mmm external/ashmem/client USER-NAME@MACHINE-NAME:~/Android$ make snod這樣,打包好的Android系統(tǒng)鏡像文件system.img就包含我們前面創(chuàng)建的Server端應(yīng)用程序SharedBufferServer和Client端應(yīng)用程序SharedBufferClient了。
至此,我們就可以運行模擬器來驗證我們的程序了。關(guān)于如何在Android源代碼工程中運行模擬器,請參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文。
執(zhí)行以下命令啟動模擬器:
USER-NAME@MACHINE-NAME:~/Android$ emulator模擬器運行起來后,就可以通過adb shell命令連上它:
USER-NAME@MACHINE-NAME:~/Android$ adb shell最后,進(jìn)入到/system/bin/目錄下:
luo@ubuntu-11-04:~/Android$ adb shell root@android:/ # cd system/bin進(jìn)入到/system/bin/目錄后,首先在后臺中運行Server端應(yīng)用程序SharedBufferServer:
root@android:/system/bin # ./SharedBufferServer &然后再在前臺中重復(fù)運行Client端應(yīng)用程序SharedBufferClient,以便驗證程序的正確性:
root@android:/system/bin # ./SharedBufferClient The value of the shared buffer is 0. Add value 1 to the shared buffer. root@android:/system/bin # ./SharedBufferClient The value of the shared buffer is 1. Add value 1 to the shared buffer. root@android:/system/bin # ./SharedBufferClient The value of the shared buffer is 2. Add value 1 to the shared buffer. root@android:/system/bin # ./SharedBufferClient The value of the shared buffer is 3. Add value 1 to the shared buffer.如果我們看到這樣的輸出,就說明我們成功地在Server端應(yīng)用程序SharedBufferServer和Client端應(yīng)用程序SharedBufferClietn共享內(nèi)存中的數(shù)據(jù)了。
至此,Android系統(tǒng)匿名共享內(nèi)存的C++調(diào)用接口MemoryHeapBase和MemoryBase就分析完成了。