2012-10-08 46 views
0

我一直在今天玩弄内存映射在VC++ 2008和我还没有完全了解如何使用它,或者如果它是我的目的是正确的。我的目标是快速读取非常大的二进制文件。读取数据的存储器映射块放入结构

我有一个结构:

typedef struct _data 
{ 
    int number; 
    char character[512]; 
    float *entries; 
}Data; 

这是写很多很多次到文件中。 “entries”变量是一个浮点小数数组。在写入这个文件(10000个数据结构,每个“entries”数组是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 
    } 
} 

在函数结束时,内存映射和我返回一个指向该内存块“PBUF”开始后,我不知道是什么这样做才能够将此内存块读回到我的数据结构中。所以最终我想将这块内存传回到我的10000个数据结构项的数组中。当然,我可能会这样做是完全错误的......

+0

将指针写入文件通常没有意义。文件内容的实际格式是什么? –

+0

@哈里约翰斯顿:该文件的内容是二进制数据结构集 – foboi1122

+0

所以实际的浮动不在任何地方的文件?你能告诉我们用于写入文件的代码吗? –

回答

5

处理内存映射文件与处理任何其他类型的内存指针真的没有什么不同。内存映射文件只是您可以使用相同名称从任何进程读取和写入的数据块。

我假设你想将文件加载到内存映射中,然后随意读取并更新它,然后将其转储到某个文件中,并以某个常规或已知的时间间隔对吗?如果是这种情况,那么只需从文件中读取并将数据复制到内存映射指针即可。稍后,您可以从地图读取数据并将其转换为内存对齐结构,并随意使用您的结构。

如果我是你,我可能会创建一个像

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 

如果您的指针指向其它内存别处,复制存储这样

data: 
    int 
    char * -> copy of original ptr 
    float * -> copy of original ptr 
512 values of char array 
number of values of float array 

所以这样你就可以“重新连载”从存储器映射到本地数据。记住,数组只是指向内存的指针。内存不必在对象中连续,因为它可能已经在另一时间分配了。您需要确保将指针所指向的实际数据复制到您的内存映射。这样做的一种常见方式是将对象直接写入内存映射中,然后使用所有扁平数组跟随对象。读它放回先阅读对象,然后通过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; 
} 

你”当将数据从结构写入内存时,可能还需要读取数据对齐。检查working with packing structures,C++ struct alignment questiondata structure alignment

如果您在阅读时未提前知道数据的大小,应将数据大小写入存储器映射开始处的已知位置以备后用。

无论如何,为了解决它的权利或不使用它的事实,我认为它是。从wikipedia

内存映射文件的主要好处是增加I/O性能,特别是在大文件上使用时。 ...内存映射过程由虚拟内存管理器处理,虚拟内存管理器负责处理页面文件。内存映射文件一次加载到内存的整个页面中。操作系统选择页面大小以获得最佳性能。由于页面文件管理是虚拟内存系统中最重要的元素之一,因此将页面大小的文件部分加载到物理内存中通常是非常高度优化的系统功能。

您将把整个内容加载到虚拟内存中,然后操作系统可以根据需要为文件分配内存和外存,从而创建“延迟加载”机制。

所有这一切都说,内存映射是共享的,所以如果跨越进程边界,你会希望它们与一个已命名的互斥体同步,所以你不会覆盖进程间的数据。

+0

我使用memcpy后会如何提前访问内存块的地址指针? – foboi1122

+0

指向内存块的指针始终指向起始地址。要访问任何后来的点,你只需要做'ptr + number'之类的东西,并且给你一个'ptr'字节的指针'number'。看看指针arithemtic上的一些文章:http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html – devshorts

+0

foboi我更新了我的答案,以帮助解决您对内存映射指针的担忧。希望这会有所帮助 – devshorts