ashmem 使用

来源:互联网 发布:知る是几段动词 编辑:程序博客网 时间:2024/05/22 09:04


在Android系统,由于内存空间一般比较有限,为了方便进程间共享数据,android提供了一种匿名共享内存的机制。为了方便的使用匿名共享内存机制,系统提供了Java的调用接口MemoryFile和C++调用接口MemoryHeapBase和MemoryBase。

       MemoryHeapBase:一般用于在进程间共享一个完整的匿名共享内存块

      MemoryBase: 一般用于在进程间共享一个匿名共享内存块的其中一部分。

   MemoryBase接口是建立在MemoryHeapBase接口的基础上实现的,他们都可以作为一个Binder对象在进程间通信。下面我们主要来分析者两个类:

 

         所有的Binder对象都必须实现IInterface接口,无论是Service端实体对象,还是Client的引用对象,通过这个接口的asBinder成员函数我们可以获得Binder对象的IBinder接口,然后通过Binder驱动程序把它传输到另外一个进程,当一个类的对象作为Service端的实体对象时,还必须实现一个目标类BnInterface,它里面有一个重要的成员函数onTransact,当Client端引用请求Server端对象执行命令时,Binder系统会调用Bnxx类的onTransact成员函数来执行具体的命令。当一个类作为Server端实体时,它需要继承于BBinder类,这是一个实现了IBinder接口的类,它里面有一个重要的成员函数transact,当我们从Server端线程中接收到Client端的请求时,会调用注册在这个线程中的BBinder对象的transact函数,将这些请求发送给Bnxx类的onTransact成员函数。

      IMemoryHeap 类主要定义几个重要的操作匿名共享内存的方法。

      frameworks/base/include/binder/IMemory.h文件中:

[html] view plain copy print?
  1. class IMemoryHeap : public IInterface  
  2. {  
  3. public:  
  4.     DECLARE_META_INTERFACE(MemoryHeap);  
  5.   
  6.     // flags returned by getFlags()  
  7.     enum {  
  8.         READ_ONLY   = 0x00000001  
  9.     };  
  10.   
  11.     virtual int         getHeapID() const = 0;  
  12.     virtual void*       getBase() const = 0;  
  13.     virtual size_t      getSize() const = 0;  
  14.     virtual uint32_t    getFlags() const = 0;  
  15.     virtual uint32_t    getOffset() const = 0;  
  16.   
  17.     // these are there just for backward source compatibility  
  18.     int32_t heapID() const { return getHeapID(); }  
  19.     void*   base() const  { return getBase(); }  
  20.     size_t  virtualSize() const { return getSize(); }  
  21. };  

 

其中几个纯虚的成员函数:

        getHeapID()  获得匿名共享内存块的打开文件描述符

        getBase()   获得匿名共享内存的基地址

        getSize()    获得匿名共享内存块的大小

 

     BnMemoryHeap 类继承于BnInterface<IMemoryHeap>

[html] view plain copy print?
  1. class BnMemoryHeap : public BnInterface<IMemoryHeap>  
  2. {  
  3. public:  
  4.     virtual status_t onTransact(   
  5.             uint32_t code,  
  6.             const Parcel& data,  
  7.             Parcel* reply,  
  8.             uint32_t flags = 0);  
  9.       
  10.     BnMemoryHeap();  
  11. protected:  
  12.     virtual ~BnMemoryHeap();  
  13. };  


  

     MemoryHeapBase  类主要用来实现上面的IMemoryHeap类的几个成员函数:

    frameworks/base/include/binder/MemoryHeapBase.h

 

[html] view plain copy print?
  1. class MemoryHeapBase : public virtual BnMemoryHeap  
  2. {  
  3. public:  
  4.     MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0);  
  5.   
  6.     MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0);  
  7.   
  8.     MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL);  
  9.   
  10.     virtual ~MemoryHeapBase();  
  11.   
  12.     /* implement IMemoryHeap interface */  
  13.     virtual int         getHeapID() const;  
  14.     virtual void*       getBase() const;  
  15.     virtual size_t      getSize() const;  
  16.     virtual uint32_t    getFlags() const;  
  17.     virtual uint32_t      getOffset() const;  
  18.   
  19.     const char*         getDevice() const;  
  20.   
  21.     void dispose();  
  22.   
  23.     status_t setDevice(const char* device) {  
  24.         if (mDevice == 0)  
  25.             mDevice = device;  
  26.         return mDevice ? NO_ERROR : ALREADY_EXISTS;  
  27.     }  
  28.   
  29. protected:  
  30.             MemoryHeapBase();  
  31.      status_t init(int fd, void *base, int size,  
  32.             int flags = 0, const char* device = NULL);  
  33. private:  
  34.     status_t mapfd(int fd, size_t size, uint32_t offset = 0);  
  35.   
  36.     int         mFD;  
  37.     size_t      mSize;  
  38.     void*       mBase;  
  39.     uint32_t    mFlags;  
  40.     const char* mDevice;  
  41.     bool        mNeedUnmap;  
  42.     uint32_t    mOffset;  
  43. };  

 

    frameworks/base/libs/binder/MemoryHeapBase.cpp

[html] view plain copy print?
  1. MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)  
  2.     : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),  
  3.       mDevice(0), mNeedUnmap(false), mOffset(0)  
  4. {  
  5.     const size_t pagesize = getpagesize();  
  6.     size = ((size + pagesize-1) & ~(pagesize-1));  
  7.     int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);  
  8.     LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));  
  9.     if (fd >= 0) {  
  10.         if (mapfd(fd, size) == NO_ERROR) {  
  11.             if (flags & READ_ONLY) {  
  12.                 ashmem_set_prot_region(fd, PROT_READ);  
  13.             }  
  14.         }  
  15.     }  
  16. }  

    它的构造函数具有三个参数:size表示要创建的匿名共享内存大小,flag用来设置匿名共享内存的属性,可是只读或读写,name是用来标示这个匿名共享内存的。

    MemoryHeapBase类创建的匿名共享内存是以页作为单位的,页的大小一般为4k,这个函数先通过getpagesize获得系统中一页内存大小,然后把size参数对其到页大小区。如果size不是页大小的整数倍时就增加它的大小,使得它的值为页大小的整数倍。

   然后就调用C接口asmem_create_region()来创建一块共享内存了。得到共享内存的文件描述符后,就需要调用mapfd函数把它映射到进程地址空间。

 

 

    BpMemoryHeap类 是MemoryHeapBase的远端代理,负责暴露接口给客户端

   BpMemoryHeap需要实现BpInterface、BpRefBase和BpBinder类,在BpRefBase里面有一个成员变量mRemote,它指向一个BpBinder对象,当BpMemoryBase需要向Server端发送请求时,就会通过这个BpBinder对象的transact函数发出。

   frameworks/base/libs/binder/IMemory.cpp:

 

[html] view plain copy print?
  1. class BpMemoryHeap : public BpInterface<IMemoryHeap>  
  2. {  
  3. public:  
  4.     BpMemoryHeap(const sp<IBinder>& impl);  
  5.     virtual ~BpMemoryHeap();  
  6.   
  7.     virtual int getHeapID() const;  
  8.     virtual void* getBase() const;  
  9.     virtual size_t getSize() const;  
  10.     virtual uint32_t getFlags() const;  
  11.     virtual uint32_t getOffset() const;  
[html] view plain copy print?
  1.   
[html] view plain copy print?
  1.   
[html] view plain copy print?
  1. static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) {  
[html] view plain copy print?
  1. return gHeapCache->find_heap(binder);  
[html] view plain copy print?
  1. }  
[html] view plain copy print?
  1. private:  
  2.     friend class IMemory;  
  3.     friend class HeapCache;  
  4.   
  5.     mutable volatile int32_t mHeapId;  
  6.     mutable void*       mBase;  
  7.     mutable size_t      mSize;  
  8.     mutable uint32_t    mFlags;  
  9.     mutable uint32_t    mOffset;  
  10.     mutable bool        mRealHeap;  
  11.     mutable Mutex       mLock;  
  12. };  

 

 我们先看看构造函数:

[html] view plain copy print?
  1. BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl)  
  2.     : BpInterface<IMemoryHeap>(impl),  
  3.         mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mOffset(0), mRealHeap(false)  
  4. {  
  5. }  

注意:构造函数的参数impl指向的值一个BpBinder对象,它里面包含了一个指向Server端的Binder对象,即MemoryHeapBase对象的引用。

 

在frameworks/base/lib/binder/IMemory.cpp:

类HeapCache的全局变量gHeapCache:它维护着当前进程中所有的MemoryHeapBase对象的引用。

由于在Client端进程中,可能会有多个引用,即多个BpMemoryHeap对象对应同一个MemoryHeapBase对象,因此当第一个BpMemoryHeap对象在本进程中映射好这块匿名共享内存之后,后面的BpMemoryHeap对象就可以直接使用,不需要再映射一次。

[html] view plain copy print?
  1. class HeapCache : public IBinder::DeathRecipient  
  2. {  
  3. public:  
  4.     HeapCache();  
  5.     virtual ~HeapCache();  
  6.   
  7.     virtual void binderDied(const wp<IBinder>& who);  
  8.   
  9.     sp<IMemoryHeap> find_heap(const sp<IBinder>& binder);  
  10.     void free_heap(const sp<IBinder>& binder);  
  11.     sp<IMemoryHeap> get_heap(const sp<IBinder>& binder);  
  12.     void dump_heaps();  
  13.   
  14. private:  
  15.     struct heap_info_t {  
  16.         sp<IMemoryHeap> heap;  
  17.         int32_t         count;  
  18.     };  
  19.   
  20.     void free_heap(const wp<IBinder>& binder);  
  21.   
  22.     Mutex mHeapCacheLock;  
  23.     KeyedVector< wp<IBinder>, heap_info_t > mHeapCache; //维护本进程中所有的BpMemoryHeap对象  
[html] view plain copy print?
  1. };  

我们主要看下find_heap函数的实现:

[html] view plain copy print?
  1. sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder)  
  2. {  
  3.     Mutex::Autolock _l(mHeapCacheLock);  
  4.     ssize_t i = mHeapCache.indexOfKey(binder);  
  5.     if (i>=0) {  // 当前进程中有其他的BpMemoryHeap对象存在  
  6.         heap_info_t& info = mHeapCache.editValueAt(i);  
  7.         LOGD_IF(VERBOSE,  
  8.                 "found binder=%p, heap=%p, size=%d, fd=%d, count=%d",  
  9.                 binder.get(), info.heap.get(),  
  10.                 static_cast<BpMemoryHeap*>(info.heap.get())->mSize,  
  11.                 static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,  
  12.                 info.count);  
  13.         android_atomic_inc(&info.count);  
  14.         return info.heap;  
  15.     } else {    // 这个进程中没有其他的BpMemoryHeap对象存在,需要将这个对象添加到mHeapCache中去  
  16.         heap_info_t info;  
  17.         info.heap = interface_cast<IMemoryHeap>(binder);  
  18.         info.count = 1;  
  19.         //LOGD("adding binder=%p, heap=%p, count=%d",  
  20.         //      binder.get(), info.heap.get(), info.count);  
  21.         mHeapCache.add(binder, info);  
  22.         return info.heap;  
  23.     }  
  24. }  

这个函数比较简单,首先以传进来的参数binder为关键字,在mHeapCache中查找,看看是否有对应的heap_info对象Info存在,如果有的话,就直接增加它的引用计数info.count值,表示这个BpBinder对象多了一个使用值。如果没有的话,那么就需要创建一个heap_info对象info,并且加到mHeapCache中去。

 

       我们发现在BpMemoryHeap的业务函数之前都会调用assertMapped()函数确定这个BpMemoryHeap对象中的匿名共享内存是否已经就绪。如果mHeapID为-1,表示还没有准备好,就需要执行一次映射匿名共享内存的操作。

        在执行映射之前,先通过find_heap()函数查看在本进程中是否有其他映射到同一个MemoryHeapBase对象的BpMemoryHeap对象存在:

[html] view plain copy print?
  1. void BpMemoryHeap::assertMapped() const  
  2. {  
  3.     if (mHeapId == -1) {  
  4.         sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());  
  5.         sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));  
  6.         heap->assertReallyMapped();  
  7.         if (heap->mBase != MAP_FAILED) {  说明这个heap对象的匿名共享内存已经映射好了,有可能是别的对象映射的  
  8.             Mutex::Autolock _l(mLock);  
  9.             if (mHeapId == -1) {          说明正在执行assertMapped函数的BpMemoryHeap对象和前面find()查找BpMemoryHeap对象不是同一个对象,需要初始化BpMemoryHeap对象的相关变量  
[html] view plain copy print?
  1.                 mBase   = heap->mBase;  
  2.                 mSize   = heap->mSize;  
  3.                 mOffset = heap->mOffset;  
  4.                 android_atomic_write( dup( heap->mHeapId ), &mHeapId );  
  5.             }  
  6.         } else {  
  7.             // something went wrong  
  8.             free_heap(binder);  
  9.         }  
  10.     }  
  11. }  

然后再调用这个BpMemoryHeap对象heap->assertReallyMapped()函数确定内部的共享内存是否已经映射了。
如果mHeapId为-1.表示在Server端的MemoryHeapBase对象中的匿名共享内存还没映射到本进程中来,于是就需要一个Binder进程间调用吧Server端的MemoryHeapBase对象中的匿名共享内存信息取回来。

[html] view plain copy print?
  1. void BpMemoryHeap::assertReallyMapped() const  
  2. {  
  3.     if (mHeapId == -1) {  
  4.         Parcel data, reply;  
  5.         data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());  
  6.         status_t err = remote()->transact(HEAP_ID, data, &reply);  
  7.         int parcel_fd = reply.readFileDescriptor();  
  8.         ssize_t size = reply.readInt32();  
  9.         uint32_t flags = reply.readInt32();  
  10.         uint32_t offset = reply.readInt32();  
  11.   
  12.         int fd = dup( parcel_fd );  
  13.         int access = PROT_READ;  
  14.         if (!(flags & READ_ONLY)) {  
  15.             access |= PROT_WRITE;  
  16.         }  
  17.   
  18.         Mutex::Autolock _l(mLock);  
  19.         if (mHeapId == -1) {  
  20.             mRealHeap = true;  
  21.             mBase = mmap(0, size, access, MAP_SHARED, fd, offset);  
  22.             if (mBase == MAP_FAILED) {  
  23.                 LOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)",  
  24.                         asBinder().get(), size, fd, strerror(errno));  
  25.                 close(fd);  
  26.             } else {  
  27.                 mSize = size;  
  28.                 mFlags = flags;  
  29.                 mOffset = offset;  
  30.                 android_atomic_write(fd, &mHeapId);  
  31.             }  
  32.         }  
  33.     }  
  34. }  

取回来的信息包括文件描述符,大小以及访问属性,获得这些信息之后就可以对它进行内存映射操作了。

 

这样BpMemoryHeap对象的匿名共享内存就已经准备就绪了,我们可以通过使用它的mBase成员变量直接访问这块匿名共享内存了。

总结:

1)new BpMemoryHeap(sp<IBinder>& impl) 对象,调用它的成员函数获取匿名共享内存信息。

2)首先通过assertMapped()函数判断是否已经准备就绪

3)成员变量mHeapCache维持着当前进程中所有BpMemoryHeap对象的引用,进程中第一个BpMemoryHeap对象需要先映射。

4)通过binder关键字在find_heap函数中查找当前进程中是否已经有其他的BpMemoryHeap对象引用,如果有就直接增加引用计数,否则需要将本BpMemoryHeap对象引用添加到mHeapCache中。

5)调用assertReallyMapped()判断上面找到的BpMemoryHeap引用是否已经映射好内存了,如果没有需要映射。

6)判断上面find_heap找到的BpMemoryHeap对象是否和当前对象是同一个对象,如果过不是同一个对象,需要设置当前对象的mBase,mSize,mOffset, mHeapId等参数。

 

MemoryBase

   我们知道MemoryBase是建立在MemoryHeapBase的基础上,他们都可以作为一个Binder对象在进程间数据共享。

    在MemoryBase类内部包含一个成员变量sp<IMemoryHeap> mHeap,通过这个成员变量来实现匿名共享内存操作。

   MemoryBase类在Server端的实现和MemoryHeapBase类在Server端的实现是类似的,只需要将IMemory类换成IMemoryHeap类,将BnMemory类换成BnMemoryHeap类,将MemoryBase类替换成MemoryHeapBase类就可以了。这里我们只见到分析IMemory和MemoryBase类:

IMemory接口

frameworks/base/include/binder/IMemory.h

[html] view plain copy print?
  1. class IMemory : public IInterface  
  2. {  
  3. public:  
  4.     DECLARE_META_INTERFACE(Memory);  
  5.      
  6.     // 获取内部MemoryHeapBase对象的IMemoryHeap接口  
  7.     virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0const = 0;  
  8.        
  9.     void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;   
  10.     void* pointer() const; // 获取内部所维护的匿名共享内存的基地址  
  11.     size_t size() const;   // 获取内部所维护的匿名共享内存的大小  
  12.     ssize_t offset() const;// 获取内部所维护的匿名共享内存在整个匿名共享内存中的偏移量  
  13. };  

frameworks/base/libs/binder/IMemory.cpp

[html] view plain copy print?
  1. void* IMemory::pointer() const {  
  2.     ssize_t offset;  
  3.     sp<IMemoryHeap> heap = getMemory(&offset);  
  4.     void* const base = heap!=0 ? heap->base() : MAP_FAILED;  
  5.     if (base == MAP_FAILED)  
  6.         return 0;  
  7.     return static_cast<char*>(base) + offset;  
  8. }  

IMemory类的子类MemoryBase只需要实现getMemory成员函数就可以了。

MemoryBase实现类:

frameworks/base/include/binder/MemoryBase.h

[html] view plain copy print?
  1. class MemoryBase : public BnMemory   
  2. {  
  3. public:  
  4.     MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);  
  5.     virtual ~MemoryBase();  
  6.     virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;  
  7.   
  8. protected:  
  9.     size_t getSize() const { return mSize; }  
  10.     ssize_t getOffset() const { return mOffset; }  
  11.     const sp<IMemoryHeap>& getHeap() const { return mHeap; }  
  12.   
  13. private:  
  14.     size_t          mSize;  
  15.     ssize_t         mOffset;  
  16.     sp<IMemoryHeap> mHeap;  
  17. };  

定义在frameworks/base/libs/binder/MemoryBase.cpp

[html] view plain copy print?
  1. sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const  
  2. {  
  3.     if (offset) *offset = mOffset;  
  4.     if (size)   *size = mSize;  
  5.     return mHeap;  
  6. }  

成员函数getMemory()比较简单,就是返回内部的MemoryHeapBase对象的IMemoryHeap接口。

BpMemory客户端代理类:
frameworks/base/libs/binder/IMemory.cpp

[html] view plain copy print?
  1. class BpMemory : public BpInterface<IMemory>  
  2. {  
  3. public:  
  4.     BpMemory(const sp<IBinder>& impl);  
  5.     virtual ~BpMemory();  
  6.     virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const;  
  7.   
  8. private:  
  9.     mutable sp<IMemoryHeap> mHeap;  
  10.     mutable ssize_t mOffset;  
  11.     mutable size_t mSize;  
  12. };  

和MemoryBase类一样,它实现了IMemory类和getMemory成员函数,在成员变量中mHeap为sp<IMemoryHeap>类型,指向一个BpMemoryHeap对象。

[html] view plain copy print?
  1. sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const  
  2. {  
  3.     if (mHeap == 0) {  
  4.         Parcel data, reply;  
  5.         data.writeInterfaceToken(IMemory::getInterfaceDescriptor());  
  6.         if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {  
  7.             sp<IBinder> heap = reply.readStrongBinder();  
  8.             ssize_t o = reply.readInt32();  
  9.             size_t s = reply.readInt32();  
  10.             if (heap != 0) {  
  11.                 mHeap = interface_cast<IMemoryHeap>(heap);  
  12.                 if (mHeap != 0) {  
  13.                     mOffset = o;  
  14.                     mSize = s;  
  15.                 }  
  16.             }  
  17.         }  
  18.     }  
  19.     if (offset) *offset = mOffset;  
  20.     if (size) *size = mSize;  
  21.     return mHeap;  
  22. }  

如果成员变量mHeap的值为0,表示这个BpMemory对象尚未建立好匿名共享内存,于是通过Binder进程间调用去Server端请求建立匿名共享内存信息,最重要的是Server端的MemoryHeapBase对象的引用heap。通过这个引用可以在Client端进程中创建一个BpMemoryHeap远程接口,最后将这个BpMemoryHeap远程接口保存到成员变量mHeap中。



我们知道,传统的IPC方式传递大块内存时,一般使用共享内存的方式。在Android Binder IPC中,有着自己独特的跨进程传递方式。它也同样,避免了内存拷贝的方式,可以让内存基址和偏移在进程间不断而且方便的传递。Android传递大内存块的方式稍有不同,这些大内存块往往位于特定的设备文件中,如pmemashmem匿名共享内存:Anonymous Shared Memory,当然,也支持共享使用其它特定设备的缓冲区。pmemashmem就是AndroidLinux内核中虚拟出的两个设备,前者是物理连续的大块内存区域,通常大小在8MB~16MB之间,不同的硬件平台或产品对其定义不同,它也位于系统的RAM中,不由内核的mm管理,而是划归给虚拟的设备pmem来管理,用户空间通过设备文件与其交互,一般用于Camera;后者原理与前者相同,也是通过设备文件使用,但它可能物理上不连续,大小也通常小些,作为普通的共享之用。在使用之前,必须调用mmap将设备缓冲区映射到系统的进程内存空间。

 

具体实现则是通过libbinder.so库中的IMemoryIMemoryHeapMemoryHeapBaseMemoryHeapPmem等类实现的。将某个设备(如pmemashmem)管理的内存映射(mmap)到系统的进程内存空间,这块内存称为内存堆(MemeoryHeap

IMemoryHeap定义了获取内存堆信息的接口,这个内存堆的信息有:HeapID(亦即设备文件描述符)、经过mmap映射到进程空间的基址、内存堆大小以及访问标志。可以使用下面四个API来获得它们的值:

virtual int getHeapID() const = 0;

virtual void* getBase() const = 0;

virtual size_t getSize() const = 0;

virtual uint32_t getFlags() const = 0;

Client侧调用接口类实际上调用到子类BpMemoryHeap对象。经Binder跨进程后,请求由server侧的子类MemoryHeapBaseMemoryHeapPmem完成。类的继承关系图如下:

 

 

 

IMemoryHeap接口中定义的四个纯虚函数由子类BpMemoryHeap中重载实现,它们均是直接返回对应的成员的值(见图中的BpMemeoryHeap,上面有四个私有成员变量,下面有四个共有成员函数)。不过在返回之前,都调用了映射函数assertMapped确保已经映射:

 

void BpMemoryHeap::assertMapped() const

{

if (mHeapId == -1) {//在还没有映射的情况下才执行下面代码

sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());//asBinder实际调用的是remote(),也就是得到BpBinder对象

sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));//通过bindercache缓存中找到对应的BpMemoryHeap对象,然后调用下面一行进行真正的映射

heap->assertReallyMapped();//进行真正的映射

if (heap->mBase != MAP_FAILED) {//没有出错则继续

Mutex::Autolock _l(mLock);

if (mHeapId == -1) {//还没有更新basesizeheapID等信息,则更新它们

mBase = heap->mBase;

mSize = heap->mSize;

android_atomic_write( dup( heap->mHeapId ), &mHeapId );

}

} else {

// something went wrong

free_heap(binder);

}

}

}

见代码注释,只有在还没有映射的情况下才进行映射,判断的依据是文件描述符是否为-1。首先获取对应的BpBinder对象,然后在全局的cache中查询BpMemoryHeap对象,接着调用BpMemoryHeap对象的assertReallyMapped进行真正的映射。

全局缓存cache(即类HeapCache)中维护着一个键值表,它是从Bpinder对象的弱指针到heap_info_t结构体的映射:

struct heap_info_t {

sp<IMemoryHeap> heap;

int32_t count; //使用计数

};

 

KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;

 

HeapCachefind_heap函数首先查询其键值表,若找到则直接返回BpMemoryHeap对象强指针,并将使用计数加1;若没找到,则创建一个BpMemoryHeap对象,并将其添加到表项中:

heap_info_t info;

info.heap = interface_cast<IMemoryHeap>(binder); //binder创建一个BpMemoryHeap对象

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;

 

也就是说,在使用IMemoryHeap也就是创建其子类对象时,不是直接使用interface_cast<IMemoryHeap>(binder)进行创建,而是首先在cache中查询;若没有查到,才进行创建。也就是确保内存堆代理BpMemoryHeap对象在一个进程中的唯一性。当多次使用到某个内存堆代理时,有使用计数维护引用次数。这样做的目的是确保只对设备文件进行一次mmap映射。从assertMapped调用assertReallyMapped就可以看出来:

void BpMemoryHeap::assertReallyMapped() const

{

if (mHeapId == -1) {

Parcel data, reply;

data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());

status_t err = remote()->transact(HEAP_ID, data, &reply);

int parcel_fd = reply.readFileDescriptor();//从对方获取设备文件描述符,通常文件描述符不能跨进程,但AndroidBinder IPCLinux内核中做了特殊处理,使它们共享内核中的file节点,因此可以跨进程。

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) {//出错,则给出log信息

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);

}

}

}

}

可见,它从对方得到相关信息后,在本进程空间重新进行mmap映射。

 

server侧,类MemoryHeapBase负责处理来自Client侧的请求,它有多个构造函数,当没有指定设备文件名称或设备文件描述符时,它就默认使用ashmem上的内存,否则使用指定的设备文件的内存。在这些构造函数中,首先检查并确保size是页对齐的,然后调用打开设备文件(若还没有文件描述符fd的话),再调用mapfd成员函数对设备文件进行mmap映射。

若使用的是pmem,则由MemoryHeapBase的子类MemoryHeapPmem来处理。当然,平台开发商也可以编写其它子类,来实现对其它内存设备的支持。

libbinder库中,还有类MemoryDealer这个类用来进行内存分配管理,它包含一个内存分配器SimpleBestFitAllocator,所分配的内存块为Allocation(继承自MemoryBase),但是当前还没有使用它们。

 

一个内存堆上,往往可以分为多个区域以供调用者使用,如Camera预览中的图像的一桢。IMemory就代表中内存堆中的这块内存区域。可以由三个变量来确定它:在哪个MemoryHeap、在堆中的偏移量offset和大小size。我们可以通过getMemory函数获得该内存区域的这三样信息:

sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const

偏移量offset和大小size通过修改实参传回,而MemoryHeap则通过函数返回。为方便起见,IMemory还提供了三个方便使用的辅助函数:通过fastPointerpointer直接得到指向内存块的指针,通过size函数可以得到内存块大小。

 

不管是内存堆MemoryHeapBase还是其子类,以及其里面的内存区域,均由使用者创建。如如CameraService依赖于Camera硬件来实现各项功能,这就是平台厂商实现的CameraHardeInterface的子类(如有的平台使用的是V4L2,对应的子类是CameraHardwareV4L2)。在Camera硬件实现中,一般都需要创建MemoryHeapBaseMemoryBase对象,即server侧对象。Camera的使用者(如应用程序)在Client侧通过调用,将得到接口对象强指针,实际指向前述BpXXX(即IMemoryHeapIMemory的子类:BpMemoryHeapBpMemory)对象。Client侧在在自己的进程里进行内存映射后,通过对应接口API就可以得到heapIDbase基址和偏移量offset,于是就可以对数据(如预览、拍照和录像的数据)进行自己的处理了。因此,在serverclient侧,它们基址多半不同,但都映射到同一设备的内存上,如pmem中的一块内存。这样,就实现了大块内存的共享。也就是说,跨进程传递的过程,实际是传递设备文件描述符、基址和偏移量等信息的过程。

MemoryHeapBase

MemroyHeapBase也是Android搞的一套基于Binder机制的对内存操作的类。既然是Binder机制,那么肯定有一个服务端(Bnxxx),一个代理端Bpxxx。看看MemoryHeapBase定义:

class MemoryHeapBase : public virtual BnMemoryHeap

{

  果然,从BnMemoryHeap派生,那就是Bn端。这样就和Binder挂上钩了

//Bp端调用的函数最终都会调到Bn这来

对Binder机制不了解的,可以参考:

http://blog.csdn.net/Innost/archive/2011/01/08/6124685.aspx

  有好几个构造函数,我们看看我们使用的:

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));

//创建共享内存,ashmem_create_region这个是系统提供的,可以不管它

//设备上打开的是/dev/ashmem设备,而Host上打开的是一个tmp文件

int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);

mapfd(fd, size);//把刚才那个fd通过mmap方式得到一块内存

//不明白得去man mmap看看

mapfd完了后,mBase变量指向内存的起始位置, mSize是分配的内存大小,mFd是

ashmem_create_region返回的文件描述符

 

}

MemoryHeapBase提供了一下几个函数,可以获取共享内存的大小和位置。

getBaseID()--->返回mFd,如果为负数,表明刚才创建共享内存失败了

getBase()->返回mBase,内存位置

  getSize()->返回mSize,内存大小

有了MemoryHeapBase,又搞了一个MemoryBase,这又是一个和Binder机制挂钩的类。

唉,这个估计是一个在MemoryHeapBase上的方便类吧?因为我看见了offset

那么估计这个类就是一个能返回当前Buffer中写位置(就是offset)的方便类

这样就不用用户到处去计算读写位置了。

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;

protected:

    size_t getSize() const { return mSize; }

    ssize_t getOffset() const { return mOffset; }

    const sp<IMemoryHeap>& getHeap() const { return mHeap; }

};

好了,明白上面两个MemoryXXX,我们可以猜测下大概的使用方法了。

<!--[if !supportLists]-->l         <!--[endif]-->BnXXX端先分配BnMemoryHeapBase和BnMemoryBase,

<!--[if !supportLists]-->l         <!--[endif]-->然后把BnMemoryBase传递到BpXXX

<!--[if !supportLists]-->l         <!--[endif]-->BpXXX就可以使用BpMemoryBase得到BnXXX端分配的共享内存了。

注意,既然是进程间共享内存,那么Bp端肯定使用memcpy之类的函数来操作内存,这些函数是没有同步保护的,而且android也不可能在系统内部为这种共享内存去做增加同步保护。所以看来后续在操作这些共享内存的时候,肯定存在一个跨进程的同步保护机制。我们在后面讲实际播放的时候会碰到。

另外,这里的SharedBuffer最终会在Bp端也就是AudioFlinger那用到。


原创粉丝点击