Android系统共享内存

1.共享内存简介

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

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

### 回答1: Android C的共享内存是一种高效的IPC机制,它允许不同的进程共享内存区域,从而实现数据共享和数据传输。在Android系统中,使用共享内存有两种基本方法:POSIX共享内存和Ashmem。 POSIX共享内存(shm_open系统调用)是基于文件的IPC机制,它可以在不同的进程间共享文件系统中的内存块。在使用该方法时,首先创建并打开一个共享内存对象以便其他进程能够在其中写入或读取数据。与普通文件不同的是,该对象可以被多个进程同时访问,从而实现内存共享和数据传输。 Ashmem是Android专有的共享内存机制,它通过匿名内存映射(mmap系统调用)来创建共享内存,使多个进程可以共享相同的内存区域。在使用Ashmem时,首先在一个进程中分配一块内存区域,并将其标记为共享内存。其他进程可以通过Binder接口来获取该内存区域所对应的Ashmem文件描述符,并进一步映射内存区域,以便共享数据。 正如所见,Android C的共享内存机制提供了一种高效的IPC方法,可以在不同的进程之间实现数据共享和数据传输。但是由于共享内存存在并发访问、内存泄露等问题,因此在应用中使用时需要格外小心。 ### 回答2: Android C共享内存是一种在Android系统中用于不同进程间共享数据的机制。在多进程应用程序中,进程之间共享数据允许各个进程共同访问数据,从而提高系统的整体性能。C共享内存实现了这种数据共享的方式,允许多个进程可以同步地访问相同的内存区域,从而实现数据共享。 C共享内存操作需要用到管道和信号量等Linux中的IPC技术。进程可以通过信号量来控制对共享内存区域的访问,从而实现数据同步。同时,通过管道机制,同步地向共享内存区域写入和读出数据。在Android开发中,通常会使用NDK库和底层C语言来实现共享内存操作,可以对共享内存区域进行读写操作和管理。 通常情况下,在Android的多进程应用程序中,可以使用C共享内存来实现不同进程之间的数据共享,从而提高应用程序的整体性能和响应速度。C共享内存也可以被用于进程间的通信,例如在游戏和音视频应用程序中,可以使用共享内存来实现不同进程的交互与协作。总的来说,Android C共享内存提供了一种能够优化应用程序性能和提高用户体验的底层机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Calvin880828

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值