内存文件映射原理和简单应用

来源:互联网 发布:遗传算法 实际应用 编辑:程序博客网 时间:2024/06/06 01:33
  1. 参考博客:http://blog.csdn.net/haiross/article/details/46875211
  2. 参考博客:http://blog.csdn.net/mg0832058/article/details/5890688 内存映射文件原理探究
  3. 硬盘上文件 的位置与进程 逻辑地址空间 中一块大小相同的区域之间的一一对应,这种对应关系纯属是逻辑上的概念,物理上是不存在的,原因是进程的逻辑地址空间本身就是不存在的,在内存映射的过程中,并没有实际的数据拷贝,文件没有被载入内存,只是逻辑上被放入了内存,具体到代码,就是建立并初始化了相关的数据结构(struct address_space),这个过程有系统调用mmap()实现,所以建立内存映射的效率很高。mmap将一个文件或者其它对象映射进内存,mmap必须以PAGE_SIZE为单位进行映射,而内存也只能以页为单位进行映射,若要映射非PAGE_SIZE整数倍的地址范围,要先进行内存对齐,强行以PAGE_SIZE的倍数大小进行映射。
  4. mmap()会返回一个指针ptr,它指向进程逻辑地址空间中的一个地址,这样以后,进程无需再调用read或write对文件进行读写,而只需要通过ptr就能够操作文件。但是ptr所指向的是一个逻辑地址,要操作其中的数据,必须通过MMU(内存管理单元)将逻辑地址转换成物理地址,这个过程与内存映射无关。

    前面讲过,建立内存映射并没有实际拷贝数据,这时,MMU在地址映射表中是无法找到与ptr相对应的物理地址的,也就是MMU失败,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则会通过mmap()建立的映射关系,从硬盘上将文件读取到物理内存中,这个过程与内存映射无关。

     如果在拷贝数据时,发现物理内存不够用,则会通过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘上,如图1中过程4所示。这个过程也与内存映射无关。

  5. 效率:从代码层面上看,从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并且数据拷贝操作是由文件系统和硬件驱动实现的,理论上来说,拷贝数据的效率是一样的。但是通过内存映射的方法访问硬盘上的文件,效率要比read和write系统调用高,这是为什么呢?原因是read()是系统调用其中进行了数据拷贝,它首先将文件内容从硬盘拷贝到内核空间的一个缓冲区,然后再将这些数据拷贝到用户空间,在这个过程中,实际上完成了 两次数据拷贝 ;而mmap()也是系统调用,如前所述,mmap()中没有进行数据拷贝,真正的数据拷贝是在缺页中断处理时进行的,由于mmap()将文件直接映射到用户空间,所以中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了 一次数据拷贝 。因此,内存映射的效率要比read/write效率高。

  6. 创建文件映射示例:
  7. #include <iostream>  
  8. #include <fcntl.h>  
  9. #include <io.h>  
  10. #include <afxwin.h>  
  11. using namespace std;  
  12.   
  13. int main()  
  14. {  
  15.     //开始  
  16. //1.获得文件句柄  创建一个文件内核对象
  17.     HANDLE hFile=CreateFile(              //失败返回INVALID_HANDLE_VALUE    
  18.         "c:\\test.dat",                   //文件名  
  19.         GENERIC_READ|GENERIC_WRITE,       //对文件进行读写操作,文件权限,
  20.         FILE_SHARE_READ|FILE_SHARE_WRITE, //共享模式
  21.         NULL,                             //安全属性
  22.         OPEN_EXISTING,                    //打开已存在文件  
  23.         FILE_ATTRIBUTE_NORMAL,            //文件或设备属性或标志
  24.         NULL);                               //模板模式
  25.   
  26.     //返回值size_high,size_low分别表示文件大小的高32位/低32位  
  27.     DWORD size_low,size_high;  
  28.     size_low= GetFileSize(hFile,&size_high);   
  29. //2.创建文件的内存映射文件, 创建一个文件映射内核对象
  30.     HANDLE hMapFile=CreateFileMapping   
  31.         hFile,           //文件名  
  32.         NULL,            //安全属性
  33.         PAGE_READWRITE,  //对映射文件进行读写  
  34.         size_high,      
  35.         size_low,        //这两个参数共64位,所以支持的最大文件长度为16EB  ,一共要映射多少到内存中
  36.         L"MyFileMap");   //映射的文件名字,其他进程,可通过此名字打开共享文件  
  37.     if(hMapFile==NULL)     //失败返回NULL
  38.     {     
  39.         AfxMessageBox("Can't create file mapping.Error%d:\n",   GetLastError());     
  40.         CloseHandle(hFile);  
  41.         return 0;     
  42.     }    
  43. //3.把文件数据映射到进程的地址空间  
  44.     void* pvFile=MapViewOfFile(       //失败返回NULL
  45.         hMapFile,                     //文件映射的句柄
  46.         FILE_MAP_READ|FILE_MAP_WRITE, //权限
  47.         0,                            //高位偏移
  48.         0,                            //低位偏移    
  49.         0);                           //一次映射多少 64K,如果是0,则映射从偏移量到文件末尾。
  50.     unsigned char *p=(unsigned char*)pvFile;   
  51.   
  52.     //至此,就获得了外部文件test.dat在内存地址空间的映射,  
  53. //4.用指针p"折磨"这个文件了  
  54.     CString s;  
  55.     p[size_low-1]='!';   
  56.     p[size_low-2]='X'//修改该文件的最后两个字节(文件大小<4GB高32位为0)  
  57.     s.Format("%s",p);  
  58.     //读文件的最后3个字节  
  59.     AfxMessageBox(s);  
  60. //5.结束  
  61.     //UnmapViewOfFile(pvFile); //撤销映射  
  62.     //CloseHandle(hFile); //关闭文件   
  63.     return 0;  
  64. }
  65. 打开文件映射示例:
  66. #include<iostream>
    #include<Windows.h>
    using namespace std;
    int main()
    {
    char szBuf[100];
    //1.打开文件映射对象
    //权限,继承性,名字
    HANDLE hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,NULL,L"MyFileMap");
    if(NULL == hFileMap) return 0;
    //2.将文件映射到进程地址空间
    char * pStartAddress = (char*)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0,0,0);
    //3.取出文件内容
    memcpy(szBuf,pStartAddress,100);
    //4.取消映射
    UnmapViewOfFile(hFileMap);
    system("pause");
    return 0;
    }