5.3 C++访问接口层详解
如果想在Android进程之间共享一个完整的匿名共享内存块,可以通过调用接口MemoryHeapBase来实现;如果只是想在进程之间共享匿名共享内存块中的一部分时,可以通过调用接口MemoryBase来实现。在本节的内容中,将详细讲解C++访问接口层的基本知识。
5.3.1 接口MemoryBase
接口MemoryBase以接口MemoryHeapBase为基础,这两个接口都可以作为一个Binder对象在进程之间进行传输。因为接口MemoryHeapBase是一个Binder对象,所以拥有Server端对象(必须实现一个BnInterface接口)和Client端引用(必须要实现一个BpInterface接口)的概念。
1.服务器端实现
在Server端的实现过程中,接口MemoryHeapBase可以将所有涉及到的类分为以下3种类型。
业务相关类:即跟匿名共享内存操作相关的类,包括MemoryHeapBase、BnMemoryHeap、IMemoryHeap。
Binder进程通信类:即和Binder进程通信机制相关的类,包括IInterface、BnInterface、IBinder、BBinder、ProcessState和IPCThreadState。
智能指针类:RefBase。
在上述3种类型中,Binder进程通信类和智能指针类将在本书后面的章节中进行讲解。在接口IMemoryBase中定义了和操作匿名共享内存的几个方法,此接口在文件“frameworks\native\include\binder\IMemory.h”中定义,具体实现代码如下所示:
class IMemoryHeap : public IInterface { public: DECLARE_META_INTERFACE(MemoryHeap); //flags returned by getFlags() enum { READ_ONLY = 0x00000001 }; virtual int getHeapID() const = 0; virtual void* getBase() const = 0; virtual size_t getSize() const = 0; virtual uint32_t getFlags() const = 0; virtual uint32_t getOffset() const = 0; //these are there just for backward source compatibility int32_t heapID() const { return getHeapID(); } void* base() const { return getBase(); } size_t virtualSize() const { return getSize(); } }
在上述定义代码中,有如下3个重要的成员函数。
getHeapID:功能是获得匿名共享内存块的打开文件描述符。
getBase:功能是获得匿名共享内存块的基地址,通过这个地址可以在程序中直接访问这块共享内存。
getSize:功能是获得匿名共享内存块的大小。
类BnMemoryHeap是一个本地对象类,当Client端引用请求Server端对象执行命令时,Binder系统就会调用类BnMemoryHeap的成员函数onTransact执行具体的命令。函数onTransact在文件“frameworks\native\libs\binder\IMemory.cpp”中定义,具体实现代码如下所示:
status_t BnMemory::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case GET_MEMORY: { CHECK_INTERFACE(IMemory, data, reply); ssize_t offset; size_t size; reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() ); reply->writeInt32(offset); reply->writeInt32(size); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } }
类MemoryHeapBase继承了类BnMemoryHeap,作为Binder机制中的Server角色需要实现IMemoryBase接口,主要功能是实现类IMemoryBase中列出的成员函数,描述了一块匿名共享内存服务。类在文件“frameworks\native\include\binder\MemoryHeapBase.h”中定义,具体实现代码如下所示:
class MemoryHeapBase : public virtual BnMemoryHeap { public: enum { READ_ONLY = IMemoryHeap::READ_ONLY, //memory won't be mapped locally, but will be mapped in the remote //process. DONT_MAP_LOCALLY = 0x00000100, NO_CACHING = 0x00000200 }; /* * maps the memory referenced by fd. but DOESN'T take ownership * of the filedescriptor (it makes a copy with dup() */ MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0); /* * maps memory from the given device */ MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); /* * maps memory from ashmem, with the given name for debugging */ MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); virtual ~MemoryHeapBase(); /* implement IMemoryHeap interface */ virtual int getHeapID() const; /* virtual address of the heap. returns MAP_FAILED in case of error */ virtual void* getBase() const; virtual size_t getSize() const; virtual uint32_t getFlags() const; virtual uint32_t getOffset() const; const char* getDevice() const; /* this closes this heap -- use carefully */ void dispose(); /* this is only needed as a workaround, use only if you know * what you are doing */ status_t setDevice(const char* device) { if (mDevice == 0) mDevice = device; return mDevice ? NO_ERROR : ALREADY_EXISTS; } protected: MemoryHeapBase(); //init() takes ownership of fd status_t init(int fd, void *base, int size, int flags = 0, const char* device = NULL); private: status_t mapfd(int fd, size_t size, uint32_t offset = 0); int mFD; //是一个文件描述符,是在打开设备文件/dev/ashmem后得到的,能够描述一个匿名共享内存块 size_t mSize; //内存块的大小 void* mBase; //内存块的映射地址 uint32_t mFlags; //内存块的访问保护位 const char* mDevice; bool mNeedUnmap; uint32_t mOffset; };
类MemoryHeapBase在文件“frameworks\native\libs\binder\MemoryHeapBase.cpp”中实现,其核心功能是包含了一块匿名共享内存。具体实现代码如下所示:
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(0), mNeedUnmap(false), mOffset(0) { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); ALOGE_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); } } } }
在上述代码中,各个参数的具体说明如下所示。
size:表示要创建的匿名共享内存的大小。
flags:设置这块匿名共享内存的属性,例如可读写、只读等。
name:此参数只是作为调试信息使用的,用于标识匿名共享内存的名字,可以是空值。
接下来看MemoryHeapBase的成员函数mapfd,其功能是将得到的匿名共享内存的文件描述符映射到进程地址空间。函数mapfd在文件“frameworks\native\libs\binder\MemoryHeapBase.cpp”中定义,具体实现代码如下所示:
status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) { if (size == 0) { //try to figure out the size automatically #ifdef HAVE_ANDROID_OS //first try the PMEM ioctl pmem_region reg; int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); if (err == 0) size = reg.len; #endif if (size == 0) {//try fstat struct stat sb; if (fstat(fd, &sb) == 0) size = sb.st_size; } //if it didn't work, let mmap() fail. } if ((mFlags & DONT_MAP_LOCALLY) == 0) {//条件为true时执行系统调用mmap来执行内存映射的操作 void* base = (uint8_t*)mmap( 0,//表示由内核来决定这个匿名共享内存文件在进程地址空间的起始位置 size,//表示要映射的匿名共享内文件的大小 PROT_READ|PROT_WRITE,//表示这个匿名共享内存是可读写的 MAP_SHARED, fd, //指定要映射的匿名共享内存的文件描述符 offset//表示要从这个文件的哪个偏移位置开始映射 ); if (base == MAP_FAILED) { ALOGE("mmap(fd=%d, size=%u) failed (%s)", fd, uint32_t(size), strerror(errno)); close(fd); return -errno; } //ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); mBase = base; mNeedUnmap = true; } else { mBase = 0;//not MAP_FAILED mNeedUnmap = false; } mFD = fd; mSize = size; mOffset = offset; return NO_ERROR; }
这样在调用个函数mapfd后,会进入到内核空间的ashmem驱动程序模块中执行函数ashmem_map。最后看成员函数getHeapID、getBase和getSize的具体实现,具体实现代码如下所示:
int MemoryHeapBase::getHeapID() const { return mFD; } void* MemoryHeapBase::getBase() const { return mBase; } size_t MemoryHeapBase::getSize() const { return mSize; }
2.客户端实现
在客户端的实现过程中,接口MemoryHeapBase可以将所有涉及到的类分为如下所示的3种类型。
业务相关类:即跟匿名共享内存操作相关的类,包括BpMemoryHeap和IMemoryHeap。
Binder进程通信类:即和Binder进程通信机制相关的类,包括IInterface、BpInterface、IBinder、BpBinder、ProcessState、BpRefBase和IPCThreadState。
智能指针类:RefBase。
在上述3种类型中,Binder进程通信类和智能指针类将在本书后面的章节中进行讲解,在本章将重点介绍业务相关类。
类BpMemoryHeap是类MemoryHeapBase在Client端进程的远接接口类,当Client端进程从Service Manager获得了一个MemoryHeapBase对象的引用后,会在本地创建一个BpMemoryHeap对象来表示这个引用。类BpMemoryHeap是从RefBase类继承下来的,也要实现IMemoryHeap接口,可以和智能指针来结合使用。
类BpMemoryHeap在文件“frameworks\native\libs\binder\IMemory.cpp”中定义,具体实现代码如下所示:
class BpMemoryHeap : public BpInterface<IMemoryHeap> { public: BpMemoryHeap(const sp<IBinder>& impl); virtual ~BpMemoryHeap(); virtual int getHeapID() const; virtual void* getBase() const; virtual size_t getSize() const; virtual uint32_t getFlags() const; virtual uint32_t getOffset() const; private: friend class IMemory; friend class HeapCache; //for debugging in this module static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) { return gHeapCache->find_heap(binder); } static inline void free_heap(const sp<IBinder>& binder) { gHeapCache->free_heap(binder); } static inline sp<IMemoryHeap> get_heap(const sp<IBinder>& binder) { return gHeapCache->get_heap(binder); } static inline void dump_heaps() { gHeapCache->dump_heaps(); } void assertMapped() const; void assertReallyMapped() const; mutable volatile int32_t mHeapId; mutable void* mBase; mutable size_t mSize; mutable uint32_t mFlags; mutable uint32_t mOffset; mutable bool mRealHeap; mutable Mutex mLock;
类BpMemoryHeap对应的构造函数是BpMemoryHeap,具体实现代码如下所示:
BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl) : BpInterface<IMemoryHeap>(impl), mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false) { }
成员函数getHeapID、getBase和getSize的实现代码如下所示:
int BpMemoryHeap::getHeapID() const { assertMapped(); return mHeapId; } void* BpMemoryHeap::getBase() const { assertMapped(); return mBase; } size_t BpMemoryHeap::getSize() const { assertMapped(); return mSize; }
在使用上述成员函数之前,通过调用函数assertMapped来确保在Client端已经准备好了匿名共享内存。函数assertMapped在文件“frameworks\native\libs\binder\IMemory.cpp”中定义,具体实现代码如下所示:
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); } } }
类HeapCache在文件“frameworks\native\libs\binder\IMemory.cpp”中定义,具体实现代码如下所示:
class HeapCache : public IBinder::DeathRecipient { public: HeapCache(); virtual ~HeapCache(); virtual void binderDied(const wp<IBinder>& who); sp<IMemoryHeap> find_heap(const sp<IBinder>& binder); void free_heap(const sp<IBinder>& binder); sp<IMemoryHeap> get_heap(const sp<IBinder>& binder); void dump_heaps(); private: //For IMemory.cpp struct heap_info_t { sp<IMemoryHeap> heap; int32_t count; }; void free_heap(const wp<IBinder>& binder); Mutex mHeapCacheLock; KeyedVector< wp<IBinder>, heap_info_t > mHeapCache; }
在上述代码中定义了成员变量mHeapCache,功能是维护进程内的所有BpMemoryHeap对象。另外还提供了函数find_heap和函数get_heap来查找内部所维护的BpMemoryHeap对象,这两个函数的具体说明如下所示。
函数find_heap:如果在mHeapCache找不到相应的BpMemoryHeap对象,则把BpMemoryHeap对象加入到mHeapCache中。
函数get_heap:不会自动把BpMemoryHeap对象加入到mHeapCache中。
接下来看函数find_heap,首先以传进来的参数binder作为关键字在mHeapCache中查找,查找是否存在对应的heap_info对象info。
如果有:增加引用计数info.count的值,表示此BpBinder对象多了一个使用者。
如果没有:创建一个放到mHeapCache中的heap_info对象info。
函数find_heap在文件“frameworks\native\libs\binder\IMemory.cpp”中定义,具体实现代码如下所示:
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); ALOGD_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; //ALOGD("adding binder=%p, heap=%p, count=%d", // binder.get(), info.heap.get(), info.count); mHeapCache.add(binder, info); return info.heap; } }
由上述实现代码可知,函数find_heap是BpMemoryHeap的成员函数,能够调用全局变量gHeapCache执行查找的操作。对应的实现代码如下所示:
class BpMemoryHeap : public BpInterface<IMemoryHeap> { ...... private: static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) { return gHeapCache->find_heap(binder); }
通过调用函数find_heap得到BpMemoryHeap对象中的函数assertReallyMapped,这样可以确认它内部的匿名共享内存是否已经映射到进程空间。函数assertReallyMapped在文件“frameworks\native\libs\binder\IMemory.cpp”中定义,具体实现代码如下所示:
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(); uint32_t offset = reply.readInt32(); ALOGE_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 ); ALOGE_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, offset); if (mBase == MAP_FAILED) { ALOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)", asBinder().get(), size, fd, strerror(errno)); close(fd); } else { mSize = size; mFlags = flags; mOffset = offset; android_atomic_write(fd, &mHeapId); } } } }
5.3.2 接口MemoryBase
接口MemoryBase是建立在接口MemoryHeapBase基础上的,两者都可以作为一个Binder对象在进程之间实现数据共享。
1.在Server端的实现
首先分析类MemoryBase在Server端的实现,MemoryBase在Server端只是简单地封装了MemoryHeapBase的实现。类MemoryBase在Server端的实现跟类MemoryHeapBase在Server端的实现类似,只需在整个类图接结构中实现如下转换即可。
把类IMemory换成类ImemoryHeap。
把类BnMemory换成类BnMemoryHeap。
把类MemoryBase换成类MemoryHeapBase。
类IMemory在文件“frameworks\native\include\binder\IMemory.h”中实现,功能是定义类MemoryBase所需要的实现接口。类IMemory的实现代码如下所示:
class IMemory : public IInterface { public: DECLARE_META_INTERFACE(Memory); virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0; //helpers void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const; void* pointer() const; size_t size() const; ssize_t offset() const; }
在类IMemory中定义了如下所示的成员函数。
getMemory:功能是获取内部MemoryHeapBase对象的IMemoryHeap接口。
pointer():功能是获取内部所维护的匿名共享内存的基地址。
size():功能是获取内部所维护的匿名共享内存的大小。
offset():功能是获取内部所维护的匿名共享内存在整个匿名共享内存中的偏移量。
类IMemory在本身定义过程中实现了3个成员函数:pointer、size和offset,其子类MemoryBase只需实现成员函数getMemory即可。类IMemory的具体实现在文件“frameworks\native\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是一个本地Binder对象类,在文件“frameworks\native\include\binder\MemoryBase.h”中声明,具体实现代码如下所示:
class MemoryBase : public BnMemory { public: MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size); virtual ~MemoryBase(); virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const; protected: size_t getSize() const { return mSize; } ssize_t getOffset() const { return mOffset; } const sp<IMemoryHeap>& getHeap() const { return mHeap; } private: size_t mSize; ssize_t mOffset; sp<IMemoryHeap> mHeap; } }//namespace android #endif//ANDROID_MEMORY_BASE_H
类MemoryBase的具体实现在文件“frameworks\native\libs\binder\MemoryBase.cpp”中定义,具体实现代码如下所示:
MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap, //指向MemoryHeapBase对象,真正的匿名共享内存就是由它来维护的 ssize_t offset, //表示这个MemoryBase对象所要维护的这部分匿名共享内存在整个匿名共享内存块中的起始位置 size_t size//表示这个MemoryBase对象所要维护的这部分匿名共享内存的大小 ) : mSize(size), mOffset(offset), mHeap(heap) { } //功能是返回内部的MemoryHeapBase对象的IMemoryHeap接口 //如果传进来的参数offset和size不为NULL //会把其内部维护的这部分匿名共享内存,在整个匿名共享内存块中的偏移位置 //以及这部分匿名共享内存的大小返回给调用者 sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const { if (offset) *offset = mOffset; if (size) *size = mSize; return mHeap; }
2.MemoryBase类在Client端的实现
再来看MemoryBase类在Client端的实现,类MemoryBase在Client端的实现与类Memory HeapBase在Client端的实现类似,只需要进行如下所示的类转换即可成为MemoryHeapBase在Client端的实现。
把类IMemory换成类ImemoryHeap。
把类BpMemory换成类BpMemoryHeap。
类BpMemory用于描述类MemoryBase服务的代理对象,在文件“frameworks\native\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;//类型为IMemoryHeap,它指向的是一个BpMemoryHeap对象 mutable ssize_t mOffset;//表示BpMemory对象所要维护的匿名共享内存在整个匿名共享内存块中的起始位置 mutable size_t mSize;//表示这个BpMemory对象所要维护的这部分匿名共享内存的大小 }
类BpMemory中的成员函数getMemory在文件“frameworks\native\libs\binder\IMemory.cpp”中定义,具体实现代码如下所示:
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对象还没有建立好匿名共享内存,此时会调用一个Binder进程去Server端请求匿名共享内存信息。通过引用信息中的Server端的MemoryHeapBase对象的引用heap,可以在Client端进程中创建一个BpMemoryHeap远程接口,最后将这个BpMemoryHeap远程接口保存在成员变量mHeap中,同时从Server端获得的信息还包括这块匿名共享内存在整个匿名共享内存中的偏移位置以及大小。