多个操作系统平台都提供了内存映射文件,这是一个简单的将数据结构保存到文件中的机制。同时由于32位操作系统的进程虚拟内存最大只能4GB,用内存映射文件的方式就可以突破这个限制,可以用来打开超过4GB的大文件。
而且,内存映射文件其实就是一种共享内存机制,进程间可以通过共享内存直接访问数据。
ACE提供了ACE_MMAP_Memory_Pool类,该类代表了为内存映射文件分配内存的内存池。和ACE_Malloc模板类配合,我们就可以以平台无关的方式操纵内存映射文件了。比如:ACE_Malloc<ACE_MMAP_Memory_Pool,ACE_SYNCH_MUTEX> 。第二个参数是并发锁的策略类。
下面的例子实现了write函数,该函数负责将结构SHMRecord的数据写到内存映射文件snapshot中,并且以HashMap的方式保存。key是offset的字符串表示,value是结构的指针。
注意,结构中的pData_成员指向的内存也应该由ACE_Malloc在内存映射文件中分配。这种模式下,千万用常规思路管理内存回收,比如智能指针,有时候内存不需要回收。
#include <iostream>
#include <sstream>
using namespace std;
#include "ace/MMAP_Memory_Pool.h"
#include "ace/Malloc_T.h"
class SHMRecord
{
public:
SHMRecord():pData_(NULL){}
ACE_UINT16 type_;
ACE_UINT32 offset_;
void* pData_;
ACE_UINT32 dataLength_;
size_t size() const
{
return 2+4+4+dataLength_;
}
};
typedef
ACE_Malloc<ACE_MMAP_Memory_Pool,ACE_SYNCH_MUTEX> MAllocator;
void write()
{
MAllocator allocator("/opt/ace/freebird/snapshot");
void * pMemory=allocator.malloc(sizeof(SHMRecord));
if(pMemory==NULL)
{
cout<<"malloc failed"<<endl;
return;
}
SHMRecord* pRecord=new(pMemory) SHMRecord();
pRecord->type_=9;
pRecord->offset_=2;
pRecord->dataLength_=4;
pRecord->pData_=allocator.malloc(4);
ACE_OS::strcpy(static_cast<char*>(pRecord->pData_),"hel");
stringstream stream;
stream<<pRecord->offset_;
if(allocator.bind(stream.str().c_str(),pRecord)==-1)
{
cout<<"bind failed"<<endl;
return;
}
allocator.sync();
}
void read()
{
}
int main(void)
{
write();
read();
return 0;
}
ACE_Malloc模板类的malloc成员负责分配内存,free负责释放内存,bind负责将数据添加到HashMap中,sync负责将数据从内存写到文件中。构造函数接收文件名作为参数。
现在我们来完成read函数,该函数将负责从内存映射文件中读取数据,并且显示出来。
void read()
{
MAllocator allocator("/opt/ace/freebird/snapshot");
ACE_Malloc_LIFO_Iterator<ACE_MMAP_Memory_Pool,ACE_SYNCH_MUTEX> iter(allocator);
for(void* pData=0;iter.next(pData)!=0;iter.advance())
{
SHMRecord* pRecord=ACE_reinterpret_cast(SHMRecord*,pData);
cout<<pRecord->type_<<endl;
cout<<pRecord->offset_<<endl;
cout<<static_cast<char*>(pRecord->pData_)<<endl;
cout<<pRecord->dataLength_<<endl;
}
}
这里使用了一个后进先出的迭代器,可以遍历HashMap中的数据。当然也可以使用find方法查找指定的数据。
多个进程因而可以使用同一块共享内存,但是问题是这些内存在每个进程中的基地址可能不同。虽然大多数操作系统能够保证,但是万一遇到意外情况怎么办?在这种情况下直接保存的指针将毫无意义。
ACE提供了解决方案:
1)使用ACE_Malloc_T<ACE_MMAP_MEMORY_POOL,
ACE_NUll_Mutex,ACE_PI_Control_Block>代替前面的分配器。关键在于ACE_PI_Control_Block类,该类使我们获得了于位置无关的分配内存功能。
2)在我们的结构中使用了普通的C++指针,现在要替换成ACE_Based_Pointer_Basic<char>类型。 该类会针对不同的基地址重新计算指针。
3)迭代器也应该使用父类:
ACE_Malloc_LIFO_Iterator_T<ACE_MMAP_Memory_Pool,ACE_SYNCH_MUTEX,ACE_PI_Control_Block> iter(allocator);