2022年读取大文件的高效的方法 .pdf
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_05.gif)
《2022年读取大文件的高效的方法 .pdf》由会员分享,可在线阅读,更多相关《2022年读取大文件的高效的方法 .pdf(16页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、读 取 一 个 大 文 件 的 高 效 的 方 法 ?内存映射HANDLE hFile = CreateFile(data.dat, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)0); HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); BYTE *pData = (BYTE*)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
2、 /pData就是文件的内容CloseHandle(hMapping); CloseHandle(hFile); 8|- 8| VC+ 中使用内存映射文件处理大文件http:/ 文件操作是应用程序最为基本的功能之一,Win32 API和 MFC均提供有支持文件处理的函数和类,常用的有 Win32 API 的 CreateFile() 、WriteFile() 、ReadFile() 和 MFC提供的 CFile 类等。一般来说,以上这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百 GB、乃至几TB 的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目
3、前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的,本文下面将针对这种Windows 核心编程技术展开讨论。内存映射文件内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O 操作,这意味着在对名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - -
4、 - - - - 名师精心整理 - - - - - - - 第 1 页,共 16 页 - - - - - - - - - 文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。内存映射文件并不是简单的文件I/O 操作,实际用到
5、了Windows 的核心编程技术-内存管理。所以,如果想对内存映射文件有更深刻的认识,必须对 Windows 操作系统的内存管理机制有清楚的认识,内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。下面给出使用内存映射文件的一般方法:首先要通过CreateFile() 函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile() 将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMap
6、ping() 函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由 MapViewOfFile() 函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。这部分相对比较简单,可以通过 UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle(
7、) 关闭前面创建的文件映射对象和文件对象。内存映射文件相关函数在使用内存映射文件时,所使用的API 函数主要就是前面提到过的那几个函数,下面分别对其进行介绍:HANDLE CreateFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 16 页 - - - - - - - - - LPSECURITY_ATTRIBUTES lpSecurity
8、Attributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); 函数 CreateFile() 即使是在普通的文件操作时也经常用来创建、打开文件,在处理内存映射文件时,该函数来创建 / 打开一个文件内核对象,并将其句柄返回,在调用该函数时需要根据是否需要数据读写和文件的共享方式来设置参数dwDesiredAccess和 dwShareMode ,错误的参数设置将会导致相应操作时的失败。HANDLE CreateFileMapping(HANDLE hFile, LPSECUR
9、ITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCTSTR lpName); CreateFileMapping()函数创建一个文件映射内核对象,通过参数 hFile 指定待映射到进程地址空间的文件句柄(该句柄由CreateFile() 函数的返回值获取)。由于内存映射文件的物理存储器实际是存储于磁盘上的一个文件,而不是从系统的页文件中分配的内存,所以系统不会主动为其保留地址空间区域,也不会自动将文件的存储空间映射到该区域,为了
10、让系统能够确定对页面采取何种保护属性,需要通过参数flProtect 来设定,保护属性PAGE_READONLY、PAGE_READWRITE 和 PAGE_WRITECOPY 分别表示文件映射对象被映射后,可以读取、读写文件数据。在使用PAGE_READONLY 时,必须确保CreateFile() 采用的是GENERIC_READ 参数; PAGE_READWRITE 则要求 CreateFile() 采用的是 GENERIC_READ|GENERIC_WRITE参数;至于属性PAGE_WRITECOPY 则只需要确保CreateFile() 采用了 GENERIC_READ 和 GENE
11、RIC_WRITE 其中之一即可。DWORD 型的参数 dwMaximumSizeHigh和 dwMaximumSizeLow也是相当重要的,指定了文件的最大字节数,由于这两个参数共64 位,因此所支持的最大文件长度为16EB,几乎可以满足任何大数据量文件处理场合的要求。LPVOID MapViewOfFile(HANDLE hFileMappingObject, 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 16 页 - - - - - - - - - DWORD d
12、wDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, DWORD dwNumberOfBytesToMap); MapViewOfFile() 函数负责把文件数据映射到进程的地址空间,参数hFileMappingObject为 CreateFileMapping() 返回的文件映像对象句柄。参数 dwDesiredAccess 则再次指定了对文件数据的访问方式,而且同样要与 CreateFileMapping()函数所设置的保护属性相匹配。虽然这里一再对保护属性进行重复设置看似多余,但却可以使应用程序能更多的对数据的保护属
13、性实行有效控制。MapViewOfFile() 函数允许全部或部分映射文件,在映射时,需要指定数据文件的偏移地址以及待映射的长度。其中,文件的偏移地址由DWORD 型的参数 dwFileOffsetHigh和 dwFileOffsetLow组成的 64 位值来指定,而且必须是操作系统的分配粒度的整数倍,对于Windows 操作系统,分配粒度固定为64KB。当然,也可以通过如下代码来动态获取当前操作系统的分配粒度:SYSTEM_INFO sinf; GetSystemInfo(&sinf); DWORD dwAllocationGranularity = sinf.dwAllocationGra
14、nularity; 参数 dwNumberOfBytesToMap指定了数据文件的映射长度,这里需要特别指出的是,对于 Windows 9x 操作系统,如果 MapViewOfFile() 无法找到足够大的区域来存放整个文件映射对象,将返回空值(NULL) ;但是在 Windows 2000 下,MapViewOfFile() 只需要为必要的视图找到足够大的一个区域即可,而无须考虑整个文件映射对象的大小。在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明如下:BOOL UnmapViewOfFile(LPCVOID
15、 lpBaseAddress); 唯一的参数lpBaseAddress 指定了返回区域的基地址,必须将其设定为MapViewOfFile() 的返回值。在使用了函数MapViewOfFile() 之后,必须要有对应的UnmapViewOfFile()调用,否则在进程终止之前,保留名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 16 页 - - - - - - - - - 的区域将无法释放。除此之外, 前面还曾由CreateFile() 和 CreateFileMappin
16、g()函数创建过文件内核对象和文件映射内核对象,在进程终止之前有必要通过CloseHandle() 将其释放,否则将会出现资源泄漏的问题。除了前面这些必须的API 函数之外,在使用内存映射文件时还要根据情况来选用其他一些辅助函数。例如,在使用内存映射文件时,为了提高速度,系统将文件的数据页面进行高速缓存,而且在处理文件映射视图时不立即更新文件的磁盘映像。为解决这个问题可以考虑使用FlushViewOfFile() 函数,该函数强制系统将修改过的数据部分或全部重新写入磁盘映像,从而可以确保所有的数据更新能及时保存到磁盘。使用内存映射文件处理大文件应用示例下面结合一个具体的实例来进一步讲述内存映射
17、文件的使用方法。该实例从端口接收数据,并实时将其存放于磁盘,由于数据量大(几十GB),在此选用内存映射文件进行处理。下面给出的是位于工作线程MainProc 中的部分主要代码,该线程自程序运行时启动,当端口有数据到达时将会发出事件hEvent0 ,WaitForMultipleObjects()函数等待到该事件发生后将接收到的数据保存到磁盘,如果终止接收将发出事件hEvent1 ,事件处理过程将负责完成资源的释放和文件的关闭等工作。下面给出此线程处理函数的具体实现过程: / 创建文件内核对象,其句柄保存于hFile HANDLE hFile = CreateFile(Recv1.zip, GE
18、NERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTI AL_SCAN, NULL); / 创建文件映射内核对象,句柄保存于hFileMapping HANDLE hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READWRITE, 0, 0 x4000000, NULL); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第
19、 5 页,共 16 页 - - - - - - - - - / 释放文件内核对象CloseHandle(hFile); / 设定大小、偏移量等参数_int64 qwFileSize = 0 x4000000; _int64 qwFileOffset = 0; _int64 T = 600 * sinf.dwAllocationGranularity; DWORD dwBytesInBlock = 1000 * sinf.dwAllocationGranularity; / 将文件数据映射到进程的地址空间PBYTE pbFile = (PBYTE)MapViewOfFile(hFileMappi
20、ng, FILE_MAP_ALL_ACCESS, (DWORD)(qwFileOffset32), (DWORD)(qwFileOffset&0 xFFFFFFFF), dwBytesInBlock); while(bLoop) / 捕获事件 hEvent0 和事件 hEvent1 DWORD ret = WaitForMultipleObjects(2, hEvent, FALSE, INFINITE); ret -= WAI T_OBJECT_0; switch (ret) / 接收数据事件触发case 0: / 从端口接收数据并保存到内存映射文件nReadLen=syio_Read(po
21、rt1, pbFile + qwFileOffset, QueueLen); qwFileOffset += nReadLen; / 当数据写满60% 时,为防数据溢出,需要在其后开辟一新的映射视图if (qwFileOffset T) T = qwFileOffset + 600 * sinf.dwAllocationGranularity; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 16 页 - - - - - - - - - UnmapViewOfFile(p
22、bFile); pbFile = (PBYTE)MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, (DWORD)(qwFileOffset32), (DWORD)(qwFileOffset&0 xFFFFFFFF), dwBytesInBlock); break; / 终止事件触发case 1: bLoop = FALSE; / 从进程的地址空间撤消文件数据映像UnmapViewOfFile(pbFile); / 关闭文件映射对象CloseHandle(hFileMapping); break; 在终止事件触发处理过程中如果只简单的执行Unmap
23、ViewOfFile()和 CloseHandle() 函数将无法正确标识文件的实际大小,即如果开辟的内存映射文件为30GB ,而接收的数据只有14GB,那么上述程序执行完后,保存的文件长度仍是30GB。也就是说, 在处理完成后还要再次通过内存映射文件的形式将文件恢复到实际大小,下面是实现此要求的主要代码:/ 创建另外一个文件内核对象hFile2 = CreateFile(Recv.zip, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - -
24、- 名师精心整理 - - - - - - - 第 7 页,共 16 页 - - - - - - - - - NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTI AL_SCAN, NULL); / 以实际数据长度创建另外一个文件映射内核对象hFileMapping2 = CreateFileMapping(hFile2, NULL, PAGE_READWRITE, 0, (DWORD)(qwFileOffset&0 xFFFFFFFF), NULL); / 关闭文件内核对象CloseHandle(hFile2); / 将文件数据映射到进程的地址空间pbFile2 =
25、(PBYTE)MapViewOfFile(hFileMapping2, FILE_MAP_ALL_ACCESS, 0, 0, qwFileOffset); / 将数据从原来的内存映射文件复制到此内存映射文件memcpy(pbFile2, pbFile, qwFileOffset); file:/ 从进程的地址空间撤消文件数据映像UnmapViewOfFile(pbFile); UnmapViewOfFile(pbFile2); / 关闭文件映射对象CloseHandle(hFileMapping); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - -
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年读取大文件的高效的方法 2022 读取 文件 高效 方法
![提示](https://www.taowenge.com/images/bang_tan.gif)
限制150内