Android系统共享内存

1.共享内存简介

共享内存是进程间通讯的一种方式,通过映射一块公共内存到各自的进程空间来达到共享内存的目的。
通常进程内存空间是4G,这个大小是由内存指针长度决定的,如果指针长度32位,那么地址最大编号为0xffffffff, 为4G。
上面的内存实际指的是进程的虚拟地址空间,还需要经过内存映射才能访问到真实的物理内存,这些工作对用户是透明的,不需要用户关心,操作系统都已经帮我们做好了。
通常虚拟内存地址和物理内存地址,但是存在一种对应关系。比如,进程操作的0x12345561这块内存地址,经过OS映射之后,可能实际的物理地址是0x87888312。
下图说明了虚拟内存与物理内存之间的关系。
Android系统共享内存_第1张图片
两个不同的进程可以同时访问同一块内存吗?答案是肯定的。这就是内存共享,该机制由操作系统提供和实现。那么是如何做到的呢? Android平台上内存共享通常按如下做法实现:

  1. 进程A创建并打开一个文件(可以是设备文件/dev/ashmem),得到一个文件描述符fd.
  2. 通过mmap调用将fd映射成内存映射文件。在mmap调用中指定参数用于标识创建的是共享内存。
  3. 进程B打开同一个文件,也得到一个文件描述符,这样A和B就打开了同一个文件。
  4. 进程B也要用mmap调用指定参数表示想使用共享内存,并传递打开的fd。这样A和B就通过打开同一个文件并构造内存映射,实现进程间内存共享。

对于进程间需要传递大量数据的场景下,这种通信方式是十分高效的。

2. MemoryHeapBase与MemoryBase

Android在Native层通过MemoryHeapBase与MemoryBase两个类实现共享内存。

[–>android_media_AudioTrack.cpp]

使用MemoryHeapBase与MemoryBase分配内存十分简单,代码如下:

class AudioTrackJniStorage {
    public:
        sp<MemoryHeapBase>         mMemHeap;
        sp<MemoryBase>             mMemBase;

.......
~AudioTrackJniStorage() {
        mMemBase.clear();
        mMemHeap.clear();
    }

bool allocSharedMem(int sizeInBytes) {
		//先new一个MemoryHeapBase,再以它为参数new一个MemoryBase
		//(1) MemoryHeapBase
        mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
        if (mMemHeap->getHeapID() < 0) {
            return false;
        }
        //(2) MemoryBase
        mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
        return true;
    }

MemoryHeapBaseMemoryBase 类关系继承图如下
Android系统共享内存_第2张图片
MemoryHeapBase是一个Binder类,承担BnMemoryHeapBase的角色, 实例由服务端创建,BpMemoryHeapBase 由客户端使用。
MemoryHeapBase有多个构造函数,创建共享内存方式不同, 使用时按需选择
[–>MemoryHeapBase.cpp ]

MemoryHeapBase::MemoryHeapBase()
    : mFD(-1), mSize(0), mBase(MAP_FAILED),
      mDevice(NULL), mNeedUnmap(false), mOffset(0)
{
}

//通过ashmem设备创建共享内存,上size表示共享内存大小,flag为0, name为"AudioTrack Heap Base"
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(); //获取系统内存页大小,一般为4kb
    size = ((size + pagesize-1) & ~(pagesize-1));
    //创建共享内存, ashmem_create_region函数由libcutils提供, 真实设备上将打开/dev/ashmem设备得到一个fd
    int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
    ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
    if (fd >= 0) {
    //将通过mmap方式得到内存地址
        if (mapfd(fd, size) == NO_ERROR) {
            if (flags & READ_ONLY) {
            //设置只读方式
                ashmem_set_prot_region(fd, PROT_READ);
            }
        }
    }
}
/*	从指定设备创建共享内存
     * maps memory from the given device
     */
MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags)
    : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
      mDevice(0), mNeedUnmap(false), mOffset(0)
{
    int open_flags = O_RDWR;
    if (flags & NO_CACHING)
        open_flags |= O_SYNC;

    int fd = open(device, open_flags);
    ALOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno));
    if (fd >= 0) {
        const size_t pagesize = getpagesize();
        size = ((size + pagesize-1) & ~(pagesize-1));
        if (mapfd(fd, size) == NO_ERROR) {
            mDevice = device;
        }
    }
}
/*	映射指定文件描述符指向的内存, 使用dup()方式copy
     * maps the memory referenced by fd. but DOESN'T take ownership
     * of the filedescriptor (it makes a copy with dup()
     */
MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset)
    : 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));
    mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
}
......
}

MemoryHeapBase 类成员变量说明:

int         mFD; //ashmem_crate_region返回的文件描述符
    size_t      mSize; //所要分配内存大小
    void*       mBase;//变量指向共享内存起始地址
    uint32_t    mFlags;
    const char* mDevice; //指定设备
    bool        mNeedUnmap;
    uint32_t    mOffset; //内存偏移量

MemoryHeapBase 使用了引用计数、延迟分配物理内存(使用时才分配)等手段优化了传统内存共享方式。

MemoryBase也是一个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; } //返回偏移量
    // 返回MemoryHeapBase对象
    const sp<IMemoryHeap>& getHeap() const { return mHeap; }

private:
    size_t          mSize;
    ssize_t         mOffset;
    sp<IMemoryHeap> mHeap;
};

// ---------------------------------------------------------------------------
}; // namespace android

3. 流程总结

总结下使用MemoryHeapBaseMemoryBase实现共享内存的相关流程:

  1. 分配一块共享内存,这样两个进程可以共享这块内存
  2. 基于Binder通信,这样这两个类可以跨进程交互。

另外说明下: 这两个类没有提供同步对象保护这块共享内存, 在使用流程中必然需要提供一个跨进程的同步对象保护它。

你可能感兴趣的:(android系统分析,共享内存,MemoryHeapBase)