VC6++内存映射修改大文件方法.docx
- 文档编号:25749533
- 上传时间:2023-06-12
- 格式:DOCX
- 页数:16
- 大小:25.05KB
VC6++内存映射修改大文件方法.docx
《VC6++内存映射修改大文件方法.docx》由会员分享,可在线阅读,更多相关《VC6++内存映射修改大文件方法.docx(16页珍藏版)》请在冰豆网上搜索。
VC6++内存映射修改大文件方法
VC6++内存映射修改大文件方法
本文介绍利用VC6++内存映射文件修改大文件的方法:
在大文件内存前加入一段数据,若要使用内存映射文件,必须执行下列操作步骤:
1.创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存映射文件的文件;
2.创建一个文件映射内核对象,告诉系统该文件的大小和你打算如何访问该文件;
3.让系统将文件映射对象的全部或一部分映射到你的进程地址空间中;
当完成对内存映射文件的使用时,必须执行下面这些步骤将它清除:
1.告诉系统从你的进程的地址空间中撤消文件映射内核对象的映像;
2.关闭文件映射内核对象;
3.关闭文件内核对象;
下面将用一个实例详细介绍这些操作步骤。
一、我们打开关于A文件内核对象,并创建一个关于B文件的内核对象若要创建或打开一个文件内核对象,总是要调用CreateFile函数:
HANDLECreateFile(
PCSTRpszFileName,
DWORDdwDesiredAccess,
DWORDdwShareMode,
PSECURITY_ATTRIBUTESpsa,
DWORDdwCreationDisposition,
DWORDdwFlagsAndAttributes,
HANDLEhTemplateFile);
CreateFile函数拥有好几个参数,这里只重点介绍前3个参数,即szFileName,dwDesiredAccess和dwShareMode。
你可能会猜到,第一个参数pszFileName用于指明要创建或打开的文件的名字(包括一个选项路径),第二个参数dwDesiredAccess用于设定如何访问该文件的内容,可以设定下表所列的4个值中的一个。
值
含义
0
不能读取或写入文件的内容,当只想获得文件的属性时,请设定0
GENERIC_READ
可以从文件中读取数据
GENERIC_WRITE
可以将数据写入文件
GENERIC_READ|GENERIC_WRITE
可以从文件中读取数据,也可以将数据写入文件
当创建或打开一个文件,将它作为一个内存映射文件来使用时,请选定最有意义的一个或多个访问标志,以说明你打算如何访问文件的数据,对内存映射文件来说,必须打开用于只读访问或读写访问的文件,因此,可以分别设定GENERIC_READ或GENERIC_READ|GENERIC_WRITE,第三个参数dwShareMode告诉系统你想如何共享该文件,可以为dwShareMode设定下表所列的4个值之一:
值
含义
0
打开文件的任何尝试均将失败
FILE_SHARE_READ
使用GENERIC_WRITE打开文件的其他尝试将会失败
FILE_SHARE_WRITE
使用GENERIC_READ打开文件的其他尝试将会失败
FILE_SHARE_READFILE_SHARE_WRITE
打开文件的其他尝试将会取得成功
如果CreateFile函数成功地创建或打开指定的文件,便返回一个文件内核对象的句柄,否则返回INVALID_HANDLE_VALUE,注意能够返回句柄的大多数Windows函数如果运行失败,那么就会返回NULL,但是,CreateFile函数将返回INVALID_HANDLE_VALUE,它定义为((HANDLE)-1),
HANDLEhFile=CreateFile(".\\first.txt",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
HANDLEhmyfile=CreateFile("E:
\\my.txt",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
二、我们要分别创建两个文件映射内核对象
调用CreateFile函数,就可以将文件映像的物理存储器的位置告诉操作系统,你传递的路径名用于指明支持文件映像的物理存储器在磁盘(或网络或光盘)上的确切位置,这时,必须告诉系统,文件映射对象需要多少物理存储器,若要进行这项操作,可以调用CreateFileMapping函数:
HANDLECreateFileMapping(
HANDLEhFile,
PSECURITY_ATTRIBUTESpsa,
DWORDfdwProtect,
DWORDdwMaximumSizeHigh,
DWORDdwMaximumSizeLow,
PCTSTRpszName);
第一个参数hFile用于标识你想要映射到进程地址空间中的文件句柄,该句柄由前面调用的CreateFile函数返回,psa参数是指向文件映射内核对象的SECURITY_ATTRIBUTES结构的指针,通常传递的值是NULL(它提供默认的安全特性,返回的句柄是不能继承的)。
本章开头讲过,创建内存映射文件就像保留一个地址空间区域然后将物理存储器提交给该区域一样,因为内存映射文件的物理存储器来自磁盘上的一个文件,而不是来自从系统的页文件中分配的空间,当创建一个文件映射对象时,系统并不为它保留地址空间区域,也不将文件的存储器映射到该区域(下一节将介绍如何进行这项操作),但是,当系统将存储器映射到进程的地址空间中去时,系统必须知道应该将什么保护属性赋予物理存储器的页面,CreateFileMapping函数的fdwProtect参数使你能够设定这些保护属性,大多数情况下,可以设定下表中列出的3个保护属性之一。
使用fdwProtect参数设定的部分保护属性
保护属性
含义
PAGE_READONLY
当文件映射对象被映射时,可以读取文件的数据,必须已经将GENERIC_READ传递给CreateFile函数。
PAGE_READWRITE
当文件映射对象被映射时,可以读取和写入文件的数据,必须已经将GENERIC_READ|GENERIC_WRITE传递给CreateFile。
PAGE_WRITECOPY
当文件映射对象被映射时,可以读取和写入文件的数据,如果写入数据,会导致页面的私有拷贝得以创建,必须已经将GENERIC_READ或GENERIC_WRITE传递给CreateFile。
在Windows98下,可以将PAGE_WRITECOPY标志传递给CreateFileMapping,这将告诉系统从页文件中提交存储器,该页文件存储器是为数据文件的数据拷贝保留的,只有修改过的页面才被写入页文件,你对该文件的数据所作的任何修改都不会重新填入原始数据文件,其最终结果是,PAGE_WRITECOPY标志的作用在Windows2000和Windows98上是相同的。
除了上面的页面保护属性外,还有4个节保护属性,你可以用OR将它们连接起来放入CreateFileMapping函数的fdwProtect参数中,节只是用于内存映射的另一个术语。
节的第一个保护属性是SEC_NOCACHE,它告诉系统,没有将文件的任何内存映射页面放入高速缓存,因此,当将数据写入该文件时,系统将更加经常地更新磁盘上的文件数据,这个标志与PAGE_NOCACHE保护属性标志一样,是供设备驱动程序开发人员使用的,应用程序通常不使用,
Windows98将忽略SEC_NOCACHE标志。
节的第二个保护属性是SEC_IMAGE,它告诉系统,你映射的文件是个可移植的可执行(PE)文件映像,当系统将该文件映射到你的进程的地址空间中时,系统要查看文件的内容,以确定将哪些保护属性赋予文件映像的各个页面,例如,PE文件的代码节(.text)通常用PAGE_EXECUTE_READ属性进行映射,而PE文件的数据节(.data)则通常用PAGE_READWRITE属性进行映射,如果设定的属性是SEC_IMAGE,则告诉系统进行文件映像的映射,并设置相应的页面保护属性。
Windows98将忽略SEC_IMAGE标志
最后两个保护属性是SEC_RESERVE和SEC_COMMIT,它们是两个互斥属性,当使用内存映射数据文件时,它们不能使用,这两个标志将在本章后面介绍,当创建内存映射数据文件时,不应该设定这些标志中的任何一个标志,CreateFileMapping将忽略这些标志。
CreateFileMapping的另外两个参数是dwMaximumSizeHigh和dwMaximumSizeLow,它们是两个最重要的参数,CreateFileMapping函数的主要作用是保证文件映射对象能够得到足够的物理存储器,这两个参数将告诉系统该文件的最大字节数,它需要两个32位的值,因为Windows支持的文件大小可以用64位的值来表示,dwMaximumSizeHigh参数用于设定较高的32位,而dwMaximumSizeLow参数则用于设定较低的32位值,对于4GB或小于4GB的文件来说,dwMaximumSizeHigh的值将始终是0。
使用64位的值,意味着Windows能够处理最大为16EB(1018字节)的文件,如果想要创建一个文件映射对象,使它能够反映文件当前的大小,那么可以为上面两个参数传递0,如果只打算读取该文件或者访问文件而不改变它的大小,那么为这两个参数传递0,如果打算将数据附加给该文件,可以选择最大的文件大小,以便为你留出一些富裕的空间,如果当前磁盘上的文件包含0字节,那么可以给CreateFileMapping函数的dwMaximumSizeHigh和dwMaximumSizeLow传递两个0,这样做就可以告诉系统,你要的文件映射对象里面的存储器为0字节,这是个错误,CreateFileMapping将返回NULL。
如果你对我们讲述的内容一直非常关注,你一定认为这里存在严重的问题,Windows支持最大为16EB的文件和文件映射对象,这当然很好,但是,怎样将这样大的文件映射到32位进程的地址空间(32位地址空间是4GB文件的上限)中去呢,下一节介绍解决这个问题的办法,当然,64位进程拥有16EB的地址空间,因此可以进行更大的文件的映射操作,但是,如果文件是个超大规模的文件,仍然会遇到类似的问题。
若要真正理解CreateFile和CreateFileMapping两个函数是如何运行的,建议你做一个下面的实验,建立下面的代码,对它进行编译,然后在一个调试程序中运行它,当你一步步执行每个语句时,你会跳到一个命令解释程序,并执行C:
\目录上的“dir”命令,当执行调试程序中的每个语句时,请注意目录中出现的变化。
intWINAPI_tWinMain(HINSTANCEhinstExe,HINSTANCE,
PTSTRpszCmdLine,intnCmdShow)
{
//Beforeexecutingthelinebelow,C:
\doesnothave
//afilecalled"MMFTest.Dat."
HANDLEhfile=CreateFile("C:
\\MMFTest.dat",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
//Beforeexecutingthelinebelow,theMMFTest.Dat
//filedoesexistbuthasafilesizeof0bytes.
HANDLEhfilemap=CreateFileMapping(hfile,NULL,PAGE_READWRITE,
0,100,NULL);
//Afterexecutingthelineabove,theMMFTest.Dat
//filehasasizeof100bytes.
//Cleanup
CloseHandle(hfilemap);
CloseHandle(hfile);
//Whentheprocessterminates,MMFTest.Datremains
//onthediskwithasizeof100bytes.
return(0);
}
如果调用CreateFileMapping函数,传递PAGE_READWRITE标志,那么系统将设法确保磁盘上的相关数据文件的大小至少与dwMaximumSizeHigh和dwMaximumSizeLow参数中设定的大小相同,如果该文件小于设定的大小,CreateFileMapping函数将扩展该文件的大小,使磁盘上的文件变大,这种扩展是必要的,这样,当以后将该文件作为内存映射文件使用时,物理存储器就已经存在了,如果正在用PAGE_READONLY或PAGE_WRITECOPY标志创建该文件映射对象,那么CreateFileMapping特定的文件大小不得大于磁盘文件的物理大小,这是因为你无法将任何数据附加给该文件。
CreateFileMapping函数的最后一个参数是pszName,它是个以0结尾的字符串,用于给该文件映射对象赋予一个名字,该名字用于与其他进程共享文件映射对象(本章后面展示了它的一个例子,第3章详细介绍了内核对象的共享操作),内存映射数据文件通常并不需要被共享,因此这个参数通常是NULL。
系统创建文件映射对象,并将用于标识该对象的句柄返回该调用线程,如果系统无法创建文件映射对象,便返回一个NULL句柄值,记住,当CreateFile运行失败时,它将返回INVALID_HANDLE_VALUE(定义为-1),当CreateFileMapping运行失败时,它返回NULL,请不要混淆这些错误值。
在本实例中创建文件映射内核对象代码如下:
HANDLEhFileMapping=CreateFileMapping(hFile,NULL,
PAGE_READONLY,0,0,NULL);
DWORDdwFileSizeHigh;
__int64qwFileSize=GetFileSize(hFile,&dwFileSizeHigh);
qwFileSize+=(((__int64)dwFileSizeHigh)<<32);
charAddMsg[]="Girl,youloveme?
Iloveyouverymuch!
";//加入的文件内容
__int64myFilesize=qwFileSize+sinf.dwAllocationGranularity;//合并后的文件大小
HANDLEhmyfilemap=CreateFileMapping(hmyfile,NULL,PAGE_READWRITE,//合并文件大小的内存映射对象
(DWORD)(myFilesize>>32),(DWORD)(myFilesize&0xFFFFFFFF),NULL);
三、将文件数据映射到地址空间
当创建了一个文件映射对象后,仍然必须让系统为文件的数据保留一个地址空间区域,并将文件的数据作为映射到该区域的物理存储器进行提交,可以通过调用MapViewOfFile函数来进行这项操作:
PVOIDMapViewOfFile(
HANDLEhFileMappingObject,
DWORDdwDesiredAccess,
DWORDdwFileOffsetHigh,
DWORDdwFileOffsetLow,
SIZE_TdwNumberOfBytesToMap);
参数hFileMappingObject用于标识文件映射对象的句柄,该句柄是前面调用CreateFileMapping或OpenFileMapping(本章后面介绍)函数返回的,参数dwDesiredAccess用于标识如何访问该数据,不错,必须再次设定如何访问文件的数据,可以设定下表所列的4个值中的一个。
表17-6值及其含义
值
含义
FILE_MAP_WRITE
可以读取和写入文件数据,CreateFileMapping函数必须通过传递PAGE_READWRITE标志来调用
FILE_MAP_READ
可以读取文件数据,CreateFileMapping函数可以通过传递下列任何一个保护属性来调用:
PAGE_READONLY,PAGE_READWRITE或PAGE_WRITECOPY。
FILE_MAP_ALL_ACCESS
与FILE_MAP_WRITE相同
FILE_MAP_COPY
可以读取和写入文件数据,如果写入文件数据,可以创建一个页面的私有拷贝,在Windows2000中,CreateFileMapping函数可以用PAGE_READONLY,PAGE_READWRITE或PAGE_WRITECOPY等保护属性中的任何一个来调用,在Windows98中,CreateFileMapping必须用PAGE_WRITECOPY来调用。
Windows要求所有这些保护属性一次又一次地重复设置,这当然有些奇怪和烦人,我认为这样做可以使应用程序更多地对数据保护属性进行控制,
剩下的3个参数与保留地址空间区域及将物理存储器映射到该区域有关,当你将一个文件映射到你的进程的地址空间中时,你不必一次性地映射整个文件,相反,可以只将文件的一小部分映射到地址空间,被映射到进程的地址空间的这部分文件称为一个视图,这可以说明MapViewOfFile是如何而得名的,
当将一个文件视图映射到进程的地址空间中时,必须规定两件事情,首先,必须告诉系统,数据文件中的哪个字节应该作为视图中的第一个字节来映射,你可以使用dwFileOffsetHigh和dwFileOffsetLow参数来进行这项操作,由于Windows支持的文件最大可达16EB,因此必须用一个64位的值来设定这个字节的位移值,这个64位值中,较高的32位传递给参数dwFileOffsetHigh,较低的32位传递给参数dwFileOffsetLow,注意,文件中的这个位移值必须是系统的分配粒度的倍数(迄今为止,Windows的所有实现代码的分配粒度均为64KB),第14章介绍了如何获取某个系统的分配粒度。
第二,必须告诉系统,数据文件有多少字节要映射到地址空间,这与设定要保留多大的地址空间区域的情况是相同的,可以使用dwNumberOfBytesToMap参数来设定这个值,如果设定的值是0,那么系统将设法把从文件中的指定位移开始到整个文件的结尾的视图映射到地址空间。
在Windows98中,如果MapViewOfFile无法找到足够大的区域来存放整个文件映射对象,那么无论需要的视图是多大,MapViewOfFile均将返回NULL。
在Windows2000中,MapViewOfFile只需要为必要的视图找到足够大的一个区域,而不管整个文件映射对象是多大。
如果在调用MapViewOfFile函数时设定了FILE_MAP_COPY标志,系统就会从系统的页文件中提交物理存储器,提交的地址空间数量由dwNumberOfBytesToMap参数决定,只要你不进行其他操作,只是从文件的映像视图中读取数据,那么系统将决不会使用页文件中的这些提交的页面,但是,如果进程中的任何线程将数据写入文件的映像视图中的任何内存地址,那么系统将从页文件中抓取已提交页面中的一个页面,将原始数据页面拷贝到该页交换文件中,然后将该拷贝的页面映射到你的进程的地址空间,从这时起,你的进程中的线程就要访问数据的本地拷贝,不能读取或修改原始数据。
当系统制作原始页面的拷贝时,系统将把页面的保护属性从PAGE_WRITECOPY改为PAGE_READWRITE,下面这个代码段就说明了这个情况:
//Openthefilethatwewanttomap.
HANDLEhFile=CreateFile(pszFileName,GENERIC_READ|GENERIC_WRITE,0,NULL,
OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
//Createafile-mappingobjectforthefile.
HANDLEhFileMapping=CreateFileMapping(hFile,NULL,PAGE_WRITECOPY,
0,0,NULL);
//Mapacopy-on-writeviewofthefile;thesystemwillcommit
//enoughphysicalstoragefromthepagingfiletoaccommodate
//theentirefile.Allpagesintheviewwillinitiallyhave
//PAGE_WRITECOPYaccess.
PBYTEpbFile=(PBYTE)MapViewOfFile(hFileMapping,FILE_MAP_COPY,
0,0,0);
//Readabytefromthemappedview.
BYTEbSomeByte=pbFile[0];
//Whenreading,thesystemdoesnottouchthecommittedpagesin
//thepagingfile.ThepagekeepsitsPAGE_WRITECOPYattribute.
//Writeabytetothemappedview.
pbFile[0]=0;
//Whenwritingforthefirsttime,thesystemgrabsacommitted
//pagefromthepagingfile,copiestheoriginalcontentsofthe
//pageattheaccessedmemoryaddress,andmapsthenewpage
//(thecopy)intotheprocess'saddressspace.Thenewpagehas
//anattributeofPAGE_READWRITE.
//Writeanotherbytetothemappedview.
pbFile[1]=0;
//BecausethisbyteisnowinaPAGE_READWRITEpage,thesystem
//simplywritesthebytetothepage(backedbythepagingfile).
//Whenfinishedusingthefile'smappedview,unmapit.
//UnmapViewOfFileisdiscussedinthene
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- VC6 内存 映射 修改 文件 方法