回到前面MemoryHeapBase类的构造函数中,将匿名共享内存映射到本进程的地址空间去后,还看继续设置这块匿名共享内存的读写属性:
-
if (fd >= 0) {
-
if (mapfd(fd, size) == NO_ERROR) {
-
if (flags & READ_ONLY) {
-
ashmem_set_prot_region(fd, PROT_READ);
-
}
-
}
-
}
上面调用mapfd函数来映射匿名共享内存时,指定这块内存是可读写的,但是如果传进来的参数flags设置了只读属性,那么还需要调用系统运行时库存层的ashmem_set_prot_region函数来设置这块匿名共享内存为只读,这个函数定义在system/core/libcutils/ashmem-dev.c文件,有兴趣的读者可以自己去研究一下。
这样,通过这个构造函数,一块匿名共享内存就建立好了,其余的三个成员函数getHeapID、getBase和getSize就简单了:
-
int MemoryHeapBase::getHeapID() const {
-
return mFD;
-
}
-
-
void* MemoryHeapBase::getBase() const {
-
return mBase;
-
}
-
-
size_t MemoryHeapBase::getSize() const {
-
return mSize;
-
}
接下来我们再来看一下MemoryHeapBase在Client端实现的类图:
这个类图中的类也是可以划分为两部分,一部分是和业务相关的,即跟匿名共享内存操作相关的类,包括BpMemoryHeap、IMemoryBase和RefBase三个类,另一部分是和Binder机制相关的,包括IInterface、BpInterface、BpRefBase、IBinder、BpBinder、ProcessState和IPCThreadState七个类。
在和匿名共享内存操作相关的类中,BpMemoryHeap类是前面分析的MemoryHeapBase类在Client端进程的远接接口类,当Client端进程从Service Manager或者其它途径获得了一个MemoryHeapBase对象的引用之后,就会在本地创建一个BpMemoryHeap对象来代表这个引用。BpMemoryHeap类同样是要实现IMemoryHeap接口,同时,它是从RefBase类继承下来的,因此,它可以与智能指针来结合使用。
在和Binder机制相关的类中,和Server端实现不一样的地方是,Client端不需要实现BnInterface和BBinder两个类,但是需要实现BpInterface、BpRefBase和BpBinder三个类。BpInterface类继承于BpRefBase类,而在BpRefBase类里面,有一个成员变量mRemote,它指向一个BpBinder对象,当BpMemoryHeap类需要向Server端对象发出请求时,它就会通过这个BpBinder对象的transact函数来发出这个请求。这里的BpBinder对象是如何知道要向哪个Server对象发出请深圳市的呢?它里面有一个成员变量mHandle,它表示的是一个Server端Binder对象的引用值,BpBinder对象就是要通过这个引用值来把请求发送到相应的Server端对象去的了,这个引用值与Server端Binder对象的对应关系是在Binder驱动程序内部维护的。这里的ProcessSate类和IPCThreadState类的作用和在Server端的作用是类似的,它们都是负责和底层的Binder驱动程序进行交互,例如,BpBinder对象的transact函数就通过线程中的IPCThreadState对象来将Client端请求发送出去的。这些实现具体可以参考
Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
一文。
这里我们主要关注BpMemoryHeap类是如何实现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;
-
-
......
-
}
先来看构造函数BpMemoryHeap的实现:
-
BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl)
-
: BpInterface<IMemoryHeap>(impl),
-
mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false)
-
{
-
}
它的实现很简单,只是初始化一下各个成员变量,例如,表示匿名共享内存文件描述符的mHeapId值初化为-1、表示匿名内共享内存基地址的mBase值初始化为MAP_FAILED以及表示匿名共享内存大小的mSize初始为为0,它们都表示在Client端进程中,这个匿名共享内存还未准备就绪,要等到第一次使用时才会去创建。这里还需要注意的一点,参数impl指向的是一个BpBinder对象,它里面包含了一个指向Server端Binder对象,即MemoryHeapBase对象的引用。
其余三个成员函数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端的匿名共享内存是已经准备就绪了的:
-
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);
-
}
-
}
-
}
在解释这个函数之前,我们需要先了解一下BpMemoryHeap是如何知道自己内部维护的这块匿名共享内存有没有准备就绪的。
在frameworks/base/libs/binder/IMemory.cpp文件中,定义了一个全局变量gHeapCache:
-
static sp<HeapCache> gHeapCache = new HeapCache();
它的类型为HeapCache,这也是一个定义在frameworks/base/libs/binder/IMemory.cpp文件的类,它里面维护了本进程中所有的MemoryHeapBase对象的引用。由于在Client端进程中,可能会有多个引用,即多个BpMemoryHeap对象,对应同一个MemoryHeapBase对象(这是由于可以用同一个BpBinder对象来创建多个BpMemoryHeap对象),因此,当第一个BpMemoryHeap对象在本进程中映射好这块匿名共享内存之后,后面的BpMemoryHeap对象就可以直接使用了,不需要再映射一次,当然重新再映射一次没有害处,但是会是多此一举,Google在设计这个类时,可以说是考虑得非常周到的。
我们来看一下HeapCache的实现:
-
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,用来维护本进程中的所有BpMemoryHeap对象,同时还提供了find_heap和get_heap函数来查找内部所维护的BpMemoryHeap对象的功能。函数find_heap和get_heap的区别是,在find_heap函数中,如果在mHeapCache找不到相应的BpMemoryHeap对象,就会把这个BpMemoryHeap对象加入到mHeapCache中去,而在get_heap函数中,则不会自动把这个BpMemoryHeap对象加入到mHeapCache中去。
这里,我们主要看一下find_heap函数的实现:
-
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;
-
}
-
}
这个函数很简单,首先它以传进来的参数binder为关键字,在mHeapCache中查找,看看是否有对应的heap_info对象info存在,如果有的话,就增加它的引用计数info.count值,表示这个BpBinder对象多了一个使用者;如果没有的话,那么就需要创建一个heap_info对象info,并且将它加放到mHeapCache中去了。1