共享内存是进程间通讯的一种方式,通过映射一块公共内存到各自的进程空间来达到共享内存的目的。
通常进程内存空间是4G,这个大小是由内存指针长度决定的,如果指针长度32位,那么地址最大编号为0xffffffff, 为4G。
上面的内存实际指的是进程的虚拟地址空间,还需要经过内存映射才能访问到真实的物理内存,这些工作对用户是透明的,不需要用户关心,操作系统都已经帮我们做好了。
通常虚拟内存地址和物理内存地址,但是存在一种对应关系。比如,进程操作的0x12345561这块内存地址,经过OS映射之后,可能实际的物理地址是0x87888312。
下图说明了虚拟内存与物理内存之间的关系。
两个不同的进程可以同时访问同一块内存吗?答案是肯定的。这就是内存共享,该机制由操作系统提供和实现。那么是如何做到的呢? Android平台上内存共享通常按如下做法实现:
对于进程间需要传递大量数据的场景下,这种通信方式是十分高效的。
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;
}
MemoryHeapBase
与MemoryBase
类关系继承图如下
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
总结下使用MemoryHeapBase
与MemoryBase
实现共享内存的相关流程:
另外说明下: 这两个类没有提供同步对象保护这块共享内存, 在使用流程中必然需要提供一个跨进程的同步对象保护它。