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

xiaoxiao2021-02-28  101

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

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

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

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

创建文件映射示例: #include <iostream>   #include <fcntl.h>   #include <io.h>   #include <afxwin.h>   using namespace std;      int main()   {       //开始   //1.获得文件句柄  创建一个文件内核对象     HANDLE hFile=CreateFile(              //失败返回INVALID_HANDLE_VALUE             "c:\\test.dat",                   //文件名           GENERIC_READ|GENERIC_WRITE,       //对文件进行读写操作,文件权限,         FILE_SHARE_READ|FILE_SHARE_WRITE, //共享模式         NULL,                             //安全属性         OPEN_EXISTING,                    //打开已存在文件           FILE_ATTRIBUTE_NORMAL,            //文件或设备属性或标志         NULL);                               //模板模式        //返回值size_high,size_low分别表示文件大小的高32位/低32位       DWORD size_low,size_high;       size_low= GetFileSize(hFile,&size_high);    //2.创建文件的内存映射文件, 创建一个文件映射内核对象     HANDLE hMapFile=CreateFileMapping            hFile,           //文件名           NULL,            //安全属性         PAGE_READWRITE,  //对映射文件进行读写           size_high,               size_low,        //这两个参数共64位,所以支持的最大文件长度为16EB  ,一共要映射多少到内存中         L"MyFileMap");   //映射的文件名字,其他进程,可通过此名字打开共享文件       if(hMapFile==NULL)     //失败返回NULL     {              AfxMessageBox("Can't create file mapping.Error%d:\n",   GetLastError());              CloseHandle(hFile);           return 0;          }     //3.把文件数据映射到进程的地址空间       void* pvFile=MapViewOfFile(       //失败返回NULL         hMapFile,                     //文件映射的句柄         FILE_MAP_READ|FILE_MAP_WRITE, //权限         0,                            //高位偏移         0,                            //低位偏移             0);                           //一次映射多少 64K,如果是0,则映射从偏移量到文件末尾。     unsigned char *p=(unsigned char*)pvFile;           //至此,就获得了外部文件test.dat在内存地址空间的映射,   //4.用指针p"折磨"这个文件了       CString s;       p[size_low-1]='!';        p[size_low-2]='X'//修改该文件的最后两个字节(文件大小<4GB高32位为0)       s.Format("%s",p);       //读文件的最后3个字节       AfxMessageBox(s);   //5.结束       //UnmapViewOfFile(pvFile); //撤销映射       //CloseHandle(hFile); //关闭文件        return 0;   }打开文件映射示例:#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; }
转载请注明原文地址: https://www.6miu.com/read-58789.html

最新回复(0)