fob*_*122 1 c++ windows memory-mapping
我今天在VC++ 2008上一直在玩内存映射,我还没有完全理解如何使用它,或者它是否符合我的目的.我的目标是快速读取一个非常大的二进制文件.
我有一个结构:
typedef struct _data
{
int number;
char character[512];
float *entries;
}Data;
Run Code Online (Sandbox Code Playgroud)
它被写入文件很多次."entries"变量是浮点小数的数组.写完这个文件后(10000个数据结构,每个"条目"数组为90000个浮点数),我试着用以下函数对这个文件进行内存映射,这样我就可以更快地读取数据.这是我到目前为止所拥有的:
void readDataMmap(char *fname, //name of file containing my data
int arraySize, //number of values in struct Data
int entrySize) //number of values in each "entries" array
{
//Read and mem map the file
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hMapFile;
char* pBuf;
int fd = open(fname, O_RDONLY);
if(fd == -1){
printf("Error: read failed");
exit(-1);
}
hFile = CreateFile((TCHAR*)fname,
GENERIC_READ, // open for reading
0, // do not share
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no template
if (hFile == INVALID_HANDLE_VALUE)
{
printf("First CreateFile failed"));
return (1);
}
hMapFile = CreateFileMapping(hFile,
NULL, // default security
PAGE_READWRITE,
0, // max. object size
0, // buffer size
NULL); // name of mapping object
if(hMapFile == ERROR_FILE_INVALID){
printf("File Mapping failed");
return(2);
}
pBuf = (char*) MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_READ, // read/write permission
0,
0,
0); //Was NULL, 0 should represent full file bytesToMap size
if (pBuf == NULL)
{
printf("Could not map view of file\n");
CloseHandle(hMapFile);
return 1;
}
//Allocate data structure
Data *inData = new Data[arraySize];
for(int i = 0; i<arraySize; i++)inData[i].entries = new float[entrySize];
int pos = 0;
for(int i = 0; i < arraySize; i++)
{
//This is where I'm not sure what to do with the memory block
}
}
Run Code Online (Sandbox Code Playgroud)
在函数结束时,在映射内存并且我返回指向内存块"pBuf"开头的指针后,我不知道该怎么做才能将这个内存块读回我的数据结构体.所以最终我想将这块内存转移回10000个数据结构条目的数组中.当然,我可能这样做完全错了......
处理内存映射文件与处理任何其他类型的内存指针没什么不同.内存映射文件只是一个数据块,您可以使用相同的名称从任何进程读取和写入.
我假设您要将文件加载到内存映射中,然后随意读取并更新它并将其以某个常规或已知间隔转储到文件中吗?如果是这种情况,那么只需从文件中读取并将数据复制到内存映射指针即可.稍后您可以从地图中读取数据并将其转换为与内存对齐的结构,并随意使用您的结构.
如果我是你,我可能会创建一些辅助方法,如
data ReadData(void *ptr)
和
void WriteData(data *ptrToData, void *ptr)
*ptr内存映射地址在哪里,*ptrToData是指向要写入内存的数据结构的指针.真的在这一点上它的内存是否映射并不重要,如果你想从加载到本地内存的文件读取你也可以这样做.
您可以使用memcpy将数据从源复制到目标,以与使用任何其他块数据完全相同的方式读取/写入它,并且可以使用指针算法来提升数据中的位置.不要担心"内存映射",它只是指向内存的指针,你可以这样对待它.
此外,由于您将要处理直接内存指针,您不需要逐个将每个元素写入映射文件,您可以将它们全部写在一个批处理中
memcpy(mapPointer, data->entries, sizeof(float)*number)
哪个副本浮动*条目大小从data->entries映射指针起始地址.显然你可以随意复制它,无论你想要什么,这只是一个例子.见http://www.devx.com/tips/Tip/13291.
要以相似的方式读取数据,但是您希望明确地将内存地址复制到已知位置,因此想象一下将结构展平.代替
data:
int
char * -> points to some address
float * -> points to some address
Run Code Online (Sandbox Code Playgroud)
如果你的指针指向其他地方的其他内存,请像这样复制内存
data:
int
char * -> copy of original ptr
float * -> copy of original ptr
512 values of char array
number of values of float array
Run Code Online (Sandbox Code Playgroud)
因此,通过这种方式,您可以将内存映射中的数据"重新序列化"到本地.请记住,数组只是指向内存的指针.内存不必在对象中是顺序的,因为它可能已在另一时间分配.您需要确保将指针指向的实际数据复制到内存映射中.执行此操作的常用方法是将对象直接写入内存映射,然后使用所有展平的数组跟踪对象.首先读取对象,然后递增指针sizeof(object)并读入下一个数组,然后再次增加指针arraysize等.
这是一个例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct data{
int size;
char items[512];
float * dataPoints;
};
void writeToBuffer(data *input, char *buffer){
int sizeOfData = sizeof(data);
int dataPointsSize = sizeof(float) * input->size;
printf("size of data %d\n", sizeOfData);
memcpy(buffer, input, sizeOfData);
printf("pointer to dataPoints of original %x\n", input->dataPoints);
memcpy(buffer + sizeOfData, input->dataPoints, dataPointsSize);
}
void readFromBuffer(data *target, char * buffer){
memcpy(target, buffer, sizeof(data));
printf("pointer to datapoints of copy %x, same as original\n", target->dataPoints);
// give ourselves a new array
target->dataPoints = (float *)malloc(target->size * sizeof(float));
// do a deep copy, since we just copied the same pointer from
// the previous data into our local
memcpy(target->dataPoints, buffer + sizeof(data), target->size * sizeof(float));
printf("pointer to datapoints of copy %x, now it's own copy\n", target->dataPoints);
}
int main(int argc, char* argv[])
{
data test;
for(unsigned int i=0;i<512;i++){
test.items[i] = i;
}
test.size = 10;
// create an array and populate the data
test.dataPoints = new float[test.size];
for(unsigned int i=0;i<test.size;i++){
test.dataPoints[i] = (float)i * (1000.0);
}
// print it out for demosntration
for(unsigned int i=0;i<test.size;i++){
printf("data point value %d: %f\n", i, test.dataPoints[i]);
}
// create a memory buffer. this is no different than the shared memory
char * memBuffer = (char*)malloc(sizeof(data) + 512 + sizeof(float) * test.size + 200);
// create a target we'll load values into
data test2;
// write the original out to the memory buffer
writeToBuffer(&test, memBuffer);
// read from the memory buffer into the target
readFromBuffer(&test2, memBuffer);
// print for demonstration
printf("copy number %d\n", test2.size);
for(int i=0;i<test2.size;i++){
printf("\tcopy value %d: %f\n", i, test2.dataPoints[i]);
}
// memory cleanup
delete memBuffer;
delete [] test.dataPoints;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在将结构中的数据写入内存时,您可能还想阅读数据对齐.检查使用打包结构,C++结构对齐问题和数据结构对齐.
如果您在读取时未提前知道数据的大小,则应将数据大小写入内存映射开头的已知位置以供以后使用.
无论如何,为了解决它是否正确使用它的事实,我认为它是.来自维基百科
内存映射文件的主要好处是提高了I/O性能,尤其是在大文件上使用时....内存映射过程由虚拟内存管理器处理,虚拟内存管理器是负责处理页面文件的相同子系统.内存映射文件一次一页地加载到内存中.操作系统选择页面大小以获得最佳性能.由于页面文件管理是虚拟内存系统的最关键元素之一,因此将文件的页面大小部分加载到物理内存中通常是非常高度优化的系统功能.
您将把整个内容加载到虚拟内存中,然后操作系统可以根据需要将文件内存和内存分页,从而创建一个"延迟加载"机制.
总而言之,内存映射是共享的,因此如果它跨越进程边界,您将需要将它们与命名的互斥锁同步,这样您就不会在进程之间覆盖数据.