现在的位置: 首页 > 移动开发 > 正文

Android开发之Memory类的分析

2019年09月04日 移动开发 ⁄ 共 11011字 ⁄ 字号 评论关闭

       在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文件中:

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

 

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

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

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

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

 

     BnMemoryHeap 类继承于BnInterface<IMemoryHeap>

class BnMemoryHeap : public BnInterface<IMemoryHeap>
{
public:
    virtual status_t onTransact( 
            uint32_t code,
            const Parcel& data,
            Parcel* reply,
            uint32_t flags = 0);
    
    BnMemoryHeap();
protected:
    virtual ~BnMemoryHeap();
};

  

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

    frameworks/base/include/binder/MemoryHeapBase.h

 

class MemoryHeapBase : public virtual BnMemoryHeap
{
public:
    MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0);

    MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0);

    MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL);

    virtual ~MemoryHeapBase();

    /* implement IMemoryHeap interface */
    virtual int         getHeapID() const;
    virtual void*       getBase() const;
    virtual size_t      getSize() const;
    virtual uint32_t    getFlags() const;
    virtual uint32_t      getOffset() const;

    const char*         getDevice() const;

    void dispose();

    status_t setDevice(const char* device) {
        if (mDevice == 0)
            mDevice = device;
        return mDevice ? NO_ERROR : ALREADY_EXISTS;
    }

protected:
            MemoryHeapBase();
     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;
    size_t      mSize;
    void*       mBase;
    uint32_t    mFlags;
    const char* mDevice;
    bool        mNeedUnmap;
    uint32_t    mOffset;
};

 

    frameworks/base/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);
    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);
            }
        }
    }
}

    它的构造函数具有三个参数: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:

 

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;
 
 
    static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) {
          return gHeapCache->find_heap(binder);
      }
private:
    friend class IMemory;
    friend class HeapCache;

    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(const sp<IBinder>& impl)
    : BpInterface<IMemoryHeap>(impl),
        mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mOffset(0), mRealHeap(false)
{
}

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

 

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

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

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

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:
    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; //维护本进程中所有的BpMemoryHeap对象
};

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

sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder)
{
    Mutex::Autolock _l(mHeapCacheLock);
    ssize_t i = mHeapCache.indexOfKey(binder);
    if (i>=0) {  // 当前进程中有其他的BpMemoryHeap对象存在
        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 {    // 这个进程中没有其他的BpMemoryHeap对象存在,需要将这个对象添加到mHeapCache中去
        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中去。

 

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

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

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) {  说明这个heap对象的匿名共享内存已经映射好了,有可能是别的对象映射的
            Mutex::Autolock _l(mLock);
            if (mHeapId == -1) {          说明正在执行assertMapped函数的BpMemoryHeap对象和前面find()查找BpMemoryHeap对象不是同一个对象,需要初始化BpMemoryHeap对象的相关变量
                mBase   = heap->mBase;
                mSize   = heap->mSize;
                mOffset = heap->mOffset;
                android_atomic_write( dup( heap->mHeapId ), &mHeapId );
            }
        } else {
            // something went wrong
            free_heap(binder);
        }
    }
}

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

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();
        ssize_t size = reply.readInt32();
        uint32_t flags = reply.readInt32();
        uint32_t offset = reply.readInt32();

        int fd = dup( parcel_fd );
        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) {
                LOGE("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);
            }
        }
    }
}

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

 

这样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

class IMemory : public IInterface
{
public:
    DECLARE_META_INTERFACE(Memory);
   
    // 获取内部MemoryHeapBase对象的IMemoryHeap接口
    virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
     
    void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const; 
    void* pointer() const; // 获取内部所维护的匿名共享内存的基地址
    size_t size() const;   // 获取内部所维护的匿名共享内存的大小
    ssize_t offset() const;// 获取内部所维护的匿名共享内存在整个匿名共享内存中的偏移量
};

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

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

MemoryBase实现类:

frameworks/base/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;
};

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

sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const
{
    if (offset) *offset = mOffset;
    if (size)   *size = mSize;
    return mHeap;
}

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

BpMemory客户端代理类:
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类一样,它实现了IMemory类和getMemory成员函数,在成员变量中mHeap为sp<IMemoryHeap>类型,指向一个BpMemoryHeap对象。

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

抱歉!评论已关闭.