1.共享記憶體簡介
共享記憶體是程序間通訊的一種方式,通過映射一塊公共記憶體到各自的程序空間來達到共享記憶體的目的。
通常程序記憶體空間是4G,這個大小是由記憶體指針長度決定的,如果指針長度32位,那麼位址最大編号為0xffffffff, 為4G。
上面的記憶體實際指的是程序的虛拟位址空間,還需要經過記憶體映射才能通路到真實的實體記憶體,這些工作對使用者是透明的,不需要使用者關心,作業系統都已經幫我們做好了。
通常虛拟記憶體位址和實體記憶體位址,但是存在一種對應關系。比如,程序操作的0x12345561這塊記憶體位址,經過OS映射之後,可能實際的實體位址是0x87888312。
下圖說明了虛拟記憶體與實體記憶體之間的關系。

兩個不同的程序可以同時通路同一塊記憶體嗎?答案是肯定的。這就是記憶體共享,該機制由作業系統提供和實作。那麼是如何做到的呢? Android平台上記憶體共享通常按如下做法實作:
- 程序A建立并打開一個檔案(可以是裝置檔案/dev/ashmem),得到一個檔案描述符fd.
- 通過mmap調用将fd映射成記憶體映射檔案。在mmap調用中指定參數用于辨別建立的是共享記憶體。
- 程序B打開同一個檔案,也得到一個檔案描述符,這樣A和B就打開了同一個檔案。
- 程序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;
}
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
3. 流程總結
總結下使用
MemoryHeapBase
與
MemoryBase
實作共享記憶體的相關流程:
- 配置設定一塊共享記憶體,這樣兩個程序可以共享這塊記憶體
- 基于Binder通信,這樣這兩個類可以跨程序互動。
另外說明下: 這兩個類沒有提供同步對象保護這塊共享記憶體, 在使用流程中必然需要提供一個跨程序的同步對象保護它。