内存映射文件的操作.docx
《内存映射文件的操作.docx》由会员分享,可在线阅读,更多相关《内存映射文件的操作.docx(15页珍藏版)》请在冰豆网上搜索。
内存映射文件的操作
内存映射文件的操作
一、概述
内存映射文件是将磁盘文件部分或全部映射到物理内存的一块地址空间,通过此地址空间,实现应用程序像访问内存一样便捷地访问磁盘文件。
通常应用程序要访问磁盘文件首先是打开文件,读文件,最后再关闭文件,这是一个非常烦琐的操作过程,尤其是在频繁访问大文件时,应用程序的复杂性和运行效率是无法容忍的,通过使用内存映射文件可以很好的解决这一问题,如图1所示。
图1内存映射文件
二、过程介绍
要正确使用内存映射文件必需执行六个过程:
一是文件打开或创建;二是创建文件映射;三是将文件数据映射到址址空间;四是解除文件数据映射;五是关闭映射文件;六是关闭文件。
上述过程主要用到四个API函数,下面对上述过程进行详细介绍:
文件打开或创建使用CreateFile函数。
调用成功返回一个文件句柄,这个句柄在后面创建映射文件时要用到。
CreatFile函数原型如下:
HANDLECreateFile(
LPCTSTRlpFileName,//文件名
DWORDdwDesiredAccess,//访问模式
DWORDdwShareMode,//共享模式
LPSECURITY_ATTRIBUTESlpSecurityAttributes,//安全属性
DWORDdwCreationDistribution,//创建方式
DWORDdwFlagsAndAttributes,//文件属性
HANDLEhTemplateFile
);
参数lpFileName为要打开的文件名,dwDesiredAccess参数描述打开文件访问方式,此参数直接影响内存映射文件的访问方式。
创建文件映射对象CreatFileMapping函数,是为打开的文件指定一块磁盘空间,此空间大小要大于或等于已打开文件大小,否则不能够完整访问文件,即能够将整个文件完整的装入该地址空间内。
CreatFileMapping函数原型如下:
HANDLECreateFileMapping(
HANDLEhFile,//文件句柄
LPSECURITY_ATTRIBUTESlpFileMappingAttributes,//安全属性
DWORDflProtect,//保护属性
DWORDdwMaximumSizeHigh,//映射对象大小高32位
DWORDdwMaximumSizeLow,//映射对象大小低32位
LPCTSTRlpName//文件映射对象名字
);
参数hFile为已打开或创建的文件句柄;flProtect参数类似CreateFile函数中dwDesiredAccess参数,用于指定保护属性,但是在这里指定的保护属性要与CreateFile函数中相对应,如在CreateFile中指定GENERIC_READ,在CreateFileMapping中只能指定PAGE_READONLY;dwMaximumSizeHigh和dwMaximumSizeLow分别是要创建内存映射文件大小的高、低32位值,即内存映射文件大小最大可达180亿GB,当然如要创建成功,必须要有180亿GB物理磁盘空间,实际上这种情况几乎用不到。
将文件数据映射到进程地址空间MapViewOfFile函数,是从已打开的文件映射对象中指定起始位置,并映射指定大小数据到进程空间,更通俗地讲就是将磁盘文件的某一部分读入内存。
MapViewOfFile函数原型如下:
LPVOIDMapViewOfFile(
HANDLEhFileMappingObject,//文件映射对象
DWORDdwDesiredAccess,//访问模式
DWORDdwFileOffsetHigh,//文件位置偏移高32位
DWORDdwFileOffsetLow,//文件位置偏移低32位
DWORDdwNumberOfBytesToMap//字节数
);
参数hFileMappingObject是CreateFileMapping函数返回的文件映射句柄;dwDesiredAccess用于指定访问方式;dwFileOffsetHigh和dwFileOffsetLow用于指定映射文件偏移位置高、低32位地址,此值小于等于文件大小,如此值为0,则系统试图将从偏移地址到文件末尾全部映射,另外特别需要注意的是此偏移值的大小要是64KB的整数倍,系统默认64K;dwNumberOfBytesToMap参数指定映射数据字节数。
函数调用成功返回进程地址空间映射数据首地址,用户程序根据此值进行数据访问。
在执行完上述三步后就可以在指定的地址空间范围内对文件进行数据操作,当操作完成后再调用UnMapViewOfFile关闭映射,通过调用UnMapViewOfFile系统将内存中的数据回写到磁盘,调用CloseHandle函数关闭映射文件和文件句柄。
三、文件操作类示例
在应用程序中频繁地调用上述API函数会使程序冗长且不易理解,如将其封装为文件操作类可以使用程序更清晰,简洁。
下面通过一个文件操作类示例详细介绍内存映射文件的使用过程。
类定义如下:
constFILE_CACHE_SIZE=200*64*1024;
//=6.4MB表示可CACHE文件大小,即文件大小小于此值文件可以打开
FILE_MAPVIEW_SIZE=64*1024;
//=64KB表示将文件映射到地址区域大小,此值为64K的整数倍,且应小于等于FILE_CACHE_SIZE
FILE_READONLY=0;
FILE_WRITE=1;
Type
TFileCache=class(TObject)
private
mMappingViewSize:
INT64;
mWriteMappingOffset:
INT64;
mWriteBufferOffset:
INT64;
mReadMappingOffset:
INT64;
FfileHandle:
integer;
FmappingHandle:
integer;
mWriteBuffer:
pointer;
mReadBuffer:
pointer;
dwWriteFizeSize:
Dword;
dwReadFileSize:
Dword;
dwShareMode:
Dword;
CacheActive:
Boolean;
public
{Publicdeclarations}
constructorCreate;
destructorDestroy;override;
functionOpenFileCache(constAFileName:
String;ShareMode:
Dword;WriteFileSize:
INT64):
boolean;
functionCloseFileCache():
boolean;
functionWriteData(WriteAddressOffset:
INT64;pData:
Dword;pDataLength:
integer):
boolean;
functionReadData(pAddressOffset:
INT64;varpData;pDataLength:
integer):
boolean;
propertyReadFileSize:
DwordreaddwReadFileSize;
propertyWriteFileSize:
DwordreaddwWriteFizeSizeWritedwWriteFizeSize;
propertyActive:
booleanreadCacheActive;
end;
TFileCache类中主定义了打开文件缓冲OpenFileCache、关闭文件缓冲CloseFileCache、写数据WriteData、读数据ReadData四个函数。
通过OpenFileCache函数可以完成打开或创建文件并打开或创建映射文件,参数AfileName用于指定文件名;ShareMode用于指定访问方式,0代表读,大于等于1为写;WriteFileSize用于指定创建文件大小,只有在写方式下起作用。
返回TRUE则打开成功,否则返回FALSE。
实现的核心代码如下:
functionTFileCache.OpenFileCache(constAFileName:
String;ShareMode:
Dword;WriteFileSize:
INT64):
boolean;
begin
if(WriteFileSize>FILE_CACHE_SIZE)and(ShareMode=1)
then
begin
raiseException.Create('WriteFileSizeToomuchtoCache');
result:
=false;
exit;
end
else
dwWriteFizeSize:
=WriteFileSize;
//打开或创建文件
caseShareModeof
0:
begin
FFileHandle:
=CreateFile(PChar(AFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
dwShareMode:
=0;
end;
1:
begin
FFileHandle:
=CreateFile(PChar(AFileName),GENERIC_READorGENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
dwShareMode:
=1;
end;
else
FFileHandle:
=CreateFile(PChar(AFileName),GENERIC_READorGENERIC_WRITE,FILE_SHARE_READ,nil,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
raiseException.Create('FileCacheShareMode=0isReadelseiswrite');
result:
=false;
exit;
end;{end打开或创建文件}
ifFFileHandle=INVALID_HANDLE_VALUEthen
begin
raiseException.Create('Errorwhenopenfile');
result:
=false;
exit;
end
else
begin
dwReadFileSize:
=GetFileSize(FFileHandle,nil);
ifdwReadFileSize>FILE_CACHE_SIZE
then
begin
raiseException.Create('FileSizeToomuchtoCache');
result:
=false;
exit;
end;
caseShareModeof
0:
FMappingHandle:
=CreateFileMapping(FFileHandle,nil,PAGE_READONLY,0,dwReadFileSize,nil);//DWORD(FILE_CACHE_SIZEshr32)
1:
FMappingHandle:
=CreateFileMapping(FFileHandle,nil,PAGE_READWRITE,0,DWORD(FILE_CACHE_SIZEand$FFFFFFFF),nil);
else
FMappingHandle:
=CreateFileMapping(FFileHandle,nil,PAGE_READWRITE,0,DWORD(FILE_CACHE_SIZEand$FFFFFFFF),nil);
raiseException.Create('FileCacheShareMode=0isReadelseiswrite');
result:
=false;
exit;
end;{endcase}
end;{end创建文件映射}
ifFMappingHandle=0then
begin
raiseException.Create('Errorwhenmappingfile');
result:
=false;
exit;
end;
mWriteMappingOffset:
=0;
mWriteBuffer:
=nil;
mWriteBufferOffset:
=0;
mReadMappingOffset:
=0;
mReadBuffer:
=nil;
CacheActive:
=true;
Result:
=true;
end;
CloseFileCache用于关闭文件映射文件以及文件句柄,并恢复类属性值,代码如下:
functionTFileCache.CloseFileCache():
boolean;
begin
if(mWriteBuffer<>nil)
then
UnmapViewOfFile(mWriteBuffer);
if(mReadBuffer<>nil)
then
UnmapViewOfFile(mReadBuffer);
if(FMappingHandle<>0)
then
CloseHandle(FMappingHandle);
if(FFileHandle<>INVALID_HANDLE_VALUE)then
begin
ifdwShareMode=0
then
SetFilePointer(FFileHandle,dwReadFileSize,nil,FILE_BEGIN)
else
SetFilePointer(FFileHandle,dwWriteFizeSize,nil,FILE_BEGIN);
SetEndofFile(FFileHandle);
CloseHandle(FFileHandle);
end;
mMappingViewSize:
=FILE_MAPVIEW_SIZE;
FFileHandle:
=INVALID_HANDLE_VALUE;
FMappingHandle:
=0;
mWriteMappingOffset:
=0;
mWriteBuffer:
=nil;
mWriteBufferOffset:
=0;
mReadMappingOffset:
=0;
mReadBuffer:
=nil;
dwReadFileSize:
=0;
dwWriteFizeSize:
=0;
dwShareMode:
=0;
CacheActive:
=false;
end;
WriteData函数实现在文件指定位置写入指定大小数据。
参数WriteAddressOffset指定文件偏移置;pData要写入数据的地址指针;pDataLength写入数据长度,其代码如下:
functionTFileCache.WriteData(WriteAddressOffset:
INT64;pData:
Dword;pDataLength:
integer):
boolean;
var
datawrote:
integer;
datacanwrite:
integer;
datatowrite:
integer;
actualdatatowrite:
integer;
MapViewOffset,WriteMapBufferOffset:
int64;
begin
datawrote:
=0;
MapViewOffset:
=trunc(WriteAddressOffset/mMappingViewSize)*mMappingViewSize;
WriteMapBufferOffset:
=WriteAddressOffsetmodmMappingViewSize;
while(datawrotebegin
datacanwrite:
=mMappingViewSize-WriteMapBufferOffset;
if(mWriteBuffer<>nil)and(datacanwrite<=0)then
begin
UnmapViewOfFile(mWriteBuffer);
mWriteBuffer:
=nil;
MapViewOffset:
=MapViewOffset+mMappingViewSize;
WriteMapBufferOffset:
=0;
end;
if(mWriteBuffer=nil)then
begin
mWriteBuffer:
=MapViewOfFile(FMappingHandle,FILE_MAP_WRITE,DWORD(mWriteMappingOffsetshr32),DWORD(MapViewOffsetand$FFFFFFFF),mMappingViewSize);
datacanwrite:
=mMappingViewSize-WriteMapBufferOffset;
if(mWriteBuffer=nil)then
begin
raiseException.Create('Errorwhenmapviewoffile.');
result:
=false;
Exit;
end;
end;
datatowrite:
=pDataLength-datawrote;
if(datacanwrite>=datatowrite)
then
actualdatatowrite:
=datatowrite
else
actualdatatowrite:
=datacanwrite;
CopyMemory(Pointer(Longint(mWriteBuffer)+WriteMapBufferOffset),Pointer(Longint(Pointer(pData))+datawrote),actualdatatowrite);
WriteMapBufferOffset:
=WriteMapBufferOffset+actualdatatowrite;
datawrote:
=datawrote+actualdatatowrite;
end;{whileend}
ifmWriteBuffer<>nil
then
begin
UnmapViewOfFile(mWriteBuffer);
mWriteBuffer:
=nil;
end;
Result:
=true;
end;
ReadData函数实现从文件指定位置读取指定大小数据。
参数pAddressOffset指定文件偏移置;pData为存放读取数据的地址指针;pDataLength读取数据长度,实现的核心代码如下:
functionTFileCache.ReadData(pAddressOffset:
INT64;varpData;pDataLength:
integer):
boolean;
var
datareaded:
integer;//已读数据大小
datacanread:
integer;//能读数据大小
datatoread:
integer;//读数据大小
actualdatatoread:
integer;//事实读数据大小
high:
Dword;
LastMapViewSize:
Dword;//最后一个文件块大小,此块值当<=文件映像大小mMappingViewSize
actualMapViewSize:
Dword;
begin
datareaded:
=0;
while(datareadedbegin
datacanread:
=mReadMappingOffset+mMappingViewSize-pAddressOffset-datareaded;
if(mReadBuffer<>nil)and((datacanread<=0)or(datacanread>mMappingViewSize))then
begin
UnmapViewOfFile(mReadBuffer);
mReadBuffer:
=nil;
end;
if(mReadBuffer=nil)then
begin
mReadMappingOffset:
=(pAddressOffset+datareaded)divmMappingViewSize*mMappingViewSize;
LastMapViewSize:
=dwReadFileSize-mReadMappingOffset;//求所剩文件大小
ifLastMapViewSize<=mMappingViewSize
then
actualMapViewSize:
=LastMapViewSize
else
actualMapViewSize:
=mMappingViewSize;
high:
=DWORD(mReadMappingOffsetshr32);
mReadBuffer:
=MapViewOfFile(FMappingHandle,FILE_MAP_READ,high,DWORD(mReadMappingOffsetand$FFFFFFFF),actualMapViewSize);
datacanread:
=mReadMappingOffset+mMappingViewSize-pAddressOffset-datareaded;
if(mReadBuffer=nil)then
begin
raiseException.Create('Errorwhenmapviewoffile.');
Result:
=false;
end;
end;
datatoread:
=pDataLength-datareaded;
if(datacanread>=datatoread)then
actualdatato