vc中用内存映射文件处理大文件

上传人:第*** 文档编号:32700127 上传时间:2018-02-12 格式:DOC 页数:7 大小:237KB
返回 下载 相关 举报
vc中用内存映射文件处理大文件_第1页
第1页 / 共7页
vc中用内存映射文件处理大文件_第2页
第2页 / 共7页
vc中用内存映射文件处理大文件_第3页
第3页 / 共7页
vc中用内存映射文件处理大文件_第4页
第4页 / 共7页
vc中用内存映射文件处理大文件_第5页
第5页 / 共7页
点击查看更多>>
资源描述

《vc中用内存映射文件处理大文件》由会员分享,可在线阅读,更多相关《vc中用内存映射文件处理大文件(7页珍藏版)》请在金锄头文库上搜索。

1、VC 中用内存映射文件处理大文件摘要: 本文通过内存映射文件的使用来对大尺寸文件进行访问操作,同时也对内存映射文件的相关概念和一般编程过程作了较为详细的介绍。关键词: 内存映射文件;大文件处理;分配粒度 引言文件操作是应用程序最为基本的功能之一,Win32 API 和 MFC 均提供有支持文件处理的函数和类,常用的有 Win32 API 的 CreateFile()、WriteFile()、ReadFile()和 MFC 提供的 CFile 类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十 GB、几百 GB、乃至几 TB 的海量存储,再以通常的文件

2、处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,本文下面将针对这种 Windows 核心编程技术展开讨论。内存映射文件概述内存文件映射也是 Windows 的一种内存管理方法,提供了一个统一的内存管理特征,使应用程序可以通过内存指针对磁盘上的文件进行访问,其过程就如同对加载了文件的内存的访问。通过文件映射这种使磁盘文件的全部或部分内容与进程虚拟地址空间的某个区域建立映射关联的能力,可以直接对被映射的文件进行访问,而不必执行文件 I/O 操作也无需对文件内容进行缓冲处理。内存文件映射的这种特性是非常适合于用来管理大尺寸文件的。在使用内存映射文

3、件进行 I/O 处理时,系统对数据的传输按页面来进行。至于内部的所有内存页面则是由虚拟内存管理器来负责管理,由其来决定内存页面何时被分页到磁盘,哪些页面应该被释放以便为其它进程提供空闲空间,以及每个进程可以拥有超出实际分配物理内存之外的多少个页面空间等等。由于虚拟内存管理器是以一种统一的方式来处理所有磁盘 I/O 的(以页面为单位对内存数据进行读写) ,因此这种优化使其有能力以足够快的速度来处理内存操作。使用内存映射文件时所进行的任何实际 I/O 交互都是在内存中进行并以标准的内存地址形式来访问。磁盘的周期性分页也是由操作系统在后台隐蔽实现的,对应用程序而言是完全透明的。内存映射文件的这种特性

4、在进行大文件的磁盘事务操作时将获得很高的效益。需要说明的是,在系统的正常的分页操作过程中,内存映射文件并非一成不变的,它将被定期更新。如果系统要使用的页面目前正被某个内存映射文件所占用,系统将释放此页面,如果页面数据尚未保存,系统将在释放页面之前自动完成页面数据到磁盘的写入。对于使用页虚拟存储管理的 Windows 操作系统,内存映射文件是其内部已有的内存管理组件的一个扩充。由可执行代码页面和数据页面组成的应用程序可根据需要由操作系统来将这些页面换进或换出内存。如果内存中的某个页面不再需要,操作系统将撤消此页面原拥用者对它的控制权,并释放该页面以供其它进程使用。只有在该页面再次成为需求页面时,

5、才会从磁盘上的可执行文件重新读入内存。同样地,当一个进程初始化启动时,内存的页面将用来存储该应用程序的静态、动态数据,一旦对它们的操作被提交,这些页面也将被备份至系统的页面文件,这与可执行文件被用来备份执行代码页面的过程是很类似的。图1 展示了代码页面和数据页面在磁盘存储器上的备份过程:图 1 进程的代码页、数据页在磁盘存储器上的备份显然,如果可以采取同一种方式来处理代码和数据页面,无疑将会提高程序的执行效率,而内存映射文件的使用恰恰可以满足此需求。对大文件的管理内存映射文件对象在关闭对象之前并没有必要撤销内存映射文件的所有视图。在对象被释放之前,所有的脏页面将自动写入磁盘。通过 CloseH

6、andle()关闭内存映射文件对象,只是释放该对象,如果内存映射文件代表的是磁盘文件,那么还需要调用标准文件 I/O 函数来将其关闭。在处理大文件处理时,内存映射文件将表示出卓越的优势,只需要消耗极少的物理资源,对系统的影响微乎其微。下面先给出内存映射文件的一般编程流程框图:图 2 使用内存映射文件的一般流程而在某些特殊行业,经常要面对十几 GB 乃至几十 GB 容量的巨型文件,而一个 32 位进程所拥有的虚拟地址空间只有 232 = 4GB,显然不能一次将文件映像全部映射进来。对于这种情况只能依次将大文件的各个部分映射到进程中的一个较小的地址空间。这需要对上面的一般流程进行适当的更改:1 )

7、映射文件开头的映像。2 )对该映像进行访问。3 )取消此映像4 )映射一个从文件中的一个更深的位移开始的新映像。5 )重复步骤 2,直到访问完全部的文件数据。下面给出一段根据此描述而写出的对大于 4GB 的文件的处理代码:/ 选择文件CFileDialog fileDlg(TRUE, *.txt, *.txt, NULL, 文本文件 (*.txt)|*.txt|, this);fileDlg.m_ofn.Flags |= OFN_FILEMUSTEXIST;fileDlg.m_ofn.lpstrTitle = 通过内存映射文件读取数据 ;if (fileDlg.DoModal() = IDOK

8、)/ 创建文件对象HANDLE hFile = CreateFile(fileDlg.GetPathName(), GENERIC_READ | GENERIC_WRITE,0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (hFile = INVALID_HANDLE_VALUE)TRACE(创建文件对象失败,错误代码:%drn, GetLastError();return;/ 创建文件映射对象HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0

9、, NULL);if (hFileMap = NULL)TRACE(创建文件映射对象失败,错误代码:%drn, GetLastError();return;/ 得到系统分配粒度SYSTEM_INFO SysInfo;GetSystemInfo(DWORD dwGran = SysInfo.dwAllocationGranularity;/ 得到文件尺寸DWORD dwFileSizeHigh;_int64 qwFileSize = GetFileSize(hFile, qwFileSize |= (_int64)dwFileSizeHigh) 0)/ 映射视图LPBYTE lpbMapAddr

10、ess = (LPBYTE)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS, (DWORD)(qwFileOffset 32), (DWORD)(qwFileOffset & 0xFFFFFFFF),dwBlockBytes);if (lpbMapAddress = NULL)TRACE(映射文件映射失败,错误代码:%drn, GetLastError();return;/ 对映射的视图进行访问for(DWORD i = 0; i dwBlockBytes; i+)BYTE temp = *(lpbMapAddress + i);/ 撤消文件映像Unmap

11、ViewOfFile(lpbMapAddress);/ 修正参数qwFileOffset += dwBlockBytes;qwFileSize -= dwBlockBytes;/ 关闭文件映射对象句柄CloseHandle(hFileMap);AfxMessageBox(成功完成对文件的访问);在本例中,首先通过 GetFileSize()得到被处理文件长度(64 位)的高 32 位和低 32 位值。然后在映射过程中设定每次映射的块大小为 1000 倍的分配粒度,如果文件长度小于 1000 倍的分配粒度时则将块大小设置为文件的实际长度。在处理过程中由映射、访问、撤消映射构成了一个循环处理。其中

12、,每处理完一个文件块后都通过关闭文件映射对象来对每个文件块进行整理。CreateFileMapping()、MapViewOfFile()等函数是专门用来进行内存文件映射处理用的。下面分别对这些关键函数进行说明:1 ) CreateFile():CreateFile()函数是一个用途非常广泛的函数, 在这里的用法并没有什么特殊的地方,但有几点需要注意:一是访问模式参数 dwDesiredAccess。该参数设置了对文件内核对象的访问类型,其允许设置的权限可以为读权限 GENERIC_READ、写权限 GENERIC_WRITE、读写权限GENERIC_READ | GENERIC_WRITE

13、和设备查询权限 0。在使用映射文件时,只能打开那些具有可读访问权限的文件,即只能应用 GENERIC_READ 和 GENERIC_READ | GENERIC_WRITE 这两种组合;另一点需要注意的是共享模式参数 dwShareMode。该参数定义了对文件内核对象的共享方式,其可能的设置为 FILE_SHARE_READ、FILE_SHARE_WRITE 和 0,并可对其组合使用。其中,设置为 0 时不允许共享对象;FILE_SHARE_READ 和 FILE_SHARE_WRITE 分别为在要求只读、只写访问的情况下才允许对象的共享。由于通过内存映射文件可以在多个进程间共享数据,因此在进

14、行这种应用时应当考虑 dwShareMode参数设置对运行结果的影响。2 ) CreateFileMapping():该函数的作用是创建一个文件映射内核对象,以告知系统文件映射对象需要多大的物理存储器。创建内存映射文件对象对系统资源几乎没有什么影响,也不会影响进程的虚拟地址空间。除了需要用来表示该对象的内部资源之外通常并不用为其分配虚拟内存,但是如果内存映射文件对象是作共享内存之用的话,就要在创建对象时由系统为内存映射文件的使用在系统页文件中保留足够的空间。函数第一个参数 hFile 为标识要映射到进程的地址空间的文件的句柄。虽然由于内存映射文件的物理存储器是来自于磁盘上的文件,而非系统的页文

15、件,使创建内存映射文件就像保留一个地址空间区域并将物理存储器提交给该区域一样。第二个参数为指向文件映射内核对象的 SECURITY_ATTRIBUTES 结构的指针,由此来决定子进程能否继承得到返回的句柄。通常为其传递 NULL 值,以默认的安全属性来禁止返回句柄的被继承。接下来的参数用于文件被映射后设定文件映像的保护属性。其可能的取值为PAGE_READONLY、PAGE_READWRITE 和 PAGE_WRITECOPY。虽然在创建文件映射对象时,系统并不为其保留地址空间区域,也不将文件的存储器映射到该区域。但是,在系统将存储器映射到进程的地址空间中去时,系统必须确切知道应赋予物理存储器

16、页面的保护属性。在设置保护属性时,必须与用CreateFile()函数打开文件时所指定的访问标识相匹配,否则将导致 CreateFileMapping()的执行失败。因此这里设置 PAGE_READWRITE 属性。除了上述三个页面保护属性外,还有 4 个区(Section)保护属性也可以一起组合使用:区保护属性 说明SEC_COMMIT 为区中的所有页面在内存中或磁盘页面文件中分配物理存储器SEC_IMAGE 告知系统,映射的文件是一个可移植的 EXE 文件映像SEC_NOCACHE告知系统,未将文件的任何内存映射文件放入高速缓存,多供硬件设备驱动程序开发人员使用SEC_RESERVE 对一个区的所有页面进行保留而不分配物理存储器后面的两个参数指定了要创建的文件映射对象的最大字节数的高 32 位值和低 32 位值,实际也就设定了文件的最大字节数(最大可以处理 16EB 的文件)。这两个参数可以满足

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 中学教育 > 职业教育

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号