内存映射文件
内存映射文件的概念:内存映射文件提供了一组独立的函数,使应用程序能够通过内存指针像访问内存一样访问磁盘上的文件。通过内存映射文件函数可以将磁盘上的文件全部或者部分映射到进程的虚拟地址空间的某个位置。一旦完成映射,对磁盘文件的访问就可以像访问内存文件一样便捷。
使用内存映射文件的好处:
a系统使用内存映射文件,以便加载和执行.exe和DLL文件。这可以大大节省页文件空间和应用程序启动运行所需的时间。
b可以使用内存映射文件来访问磁盘上的数据文件。这使你可以不必对文件执行I/O操作,并且可以不必对文件内容进行缓存。
c可以使用内存映射文件,使同一台计算机上运行的多个进程能够相互之间共享数据。注:使用内存映射文件数据传输是通过4k大小的数据页面实现的。
内存映射文件的实现原理:
先来看看windows的页式虚拟存储管理,在windows中,每一个页面在一个时刻都是处于一下三种情况之一:空闲的,挂起的还有已经提交的。
当某个页面不需要的时候windows可以将其从内存中调换出来,需要的时候页面可以重新再次从物理存储器读入内存。内存映射文件的实现原理与此相似,内存映射文件也保留了一个地址空间的区域,并根据需要将物理存储器提交到该区域。
区别是:内存映射文件存取一个磁盘文件时,它提交的物理存储器就是来自该文件~!
内存映射文件函数包括:
CreatFileMapping, OpenFileMapping, MapViewOfFile, UnmapViewOfFile和FlushViewOfFile.
使用内存映射文件的步骤分两步:
1 使用CreatFileMapping创建一个内存映射文件对象。
Invoke CreatFileMapping,hFile,lpFilemappingAttributes, flProtect,dwMaximumSizeHigh,\
dwMaximumSizeLow ,lpName
.if eax
mov hFileMap,eax
.endif
这个函数确定了映射文件的用途:
1 使用CreatFileMapping创建一个内存映射文件对象。
Invoke CreatFileMapping,hFile,lpFilemappingAttributes, flProtect,dwMaximumSizeHigh,\
dwMaximumSizeLow ,lpName
.if eax
mov hFileMap,eax
.endif
这个函数确定了映射文件的用途:
在磁盘文件建立内存映射文件还是在内存页面上建立进程间共享的映射。
通过设置第一个参数hFile的值可以实现上面的功能。如果hFile句柄是属于一个已经打开的文件,那么映射文件就在这个文件上建立。如果想建立存在于内存文件中内存映射文件供不同进程共享,则将hFile指定为-1。
lpFilemappingAttributes:内存映射文件对象是否可以被继承。不被继承使用NULL。
flProtect: 内存映射文件的保护方式,注意要在用CreatFile函数打开文件获得hFile句柄的时候必须指定相应的标志flProtect。
lpFilemappingAttributes:内存映射文件对象是否可以被继承。不被继承使用NULL。
flProtect: 内存映射文件的保护方式,注意要在用CreatFile函数打开文件获得hFile句柄的时候必须指定相应的标志flProtect。
dwMaximumSizeHigh/dwMaximumSizeLow::指定64位内存映射文件的长度。将这两参数设置为零,系统会自动将内存映射文件的大小调整到磁盘文件的大小。
lpName:内存映射文件的名字。用于进程共享的时候映射文件必须起名字。
lpName:内存映射文件的名字。用于进程共享的时候映射文件必须起名字。
使用OpenFileMapping 打开已经建立的用于进程共享的内存映射文件。
Invoke OpenFileMapping,dwDesiredAccess,bInheritHandle, lpName
.if eax
Mov hFileMap, eax
.endif
dwDesiredAccess:指定保护类型。
lpName :创建对象时使用的名字。
bInheritHandle : 继承文件的句柄。
Invoke OpenFileMapping,dwDesiredAccess,bInheritHandle, lpName
.if eax
Mov hFileMap, eax
.endif
dwDesiredAccess:指定保护类型。
lpName :创建对象时使用的名字。
bInheritHandle : 继承文件的句柄。
2 创建内存映射文件的一个视图。相当于给内存映射文件分配线形地址空间,并将线形地址与文件的内容相关联。这样应用程序可以通过存取线形地址来存取文件。
Invoke MapViewOfFile,hFileMap,dwDesiredAccess,dwFileOffsetHigh,dwFileOffsetLow,\
dwNumberOfBytesToMap
.if eax
Mov ipMemory,eax
.endif
不多说了这个函数的参数很简单。
取消映射文件:invoke UnmapViewOfFile,lpMemory
关闭映射文件:invoke CloseHandle,hFileMap
Invoke MapViewOfFile,hFileMap,dwDesiredAccess,dwFileOffsetHigh,dwFileOffsetLow,\
dwNumberOfBytesToMap
.if eax
Mov ipMemory,eax
.endif
不多说了这个函数的参数很简单。
取消映射文件:invoke UnmapViewOfFile,lpMemory
关闭映射文件:invoke CloseHandle,hFileMap
将对文件的修改立即写到磁盘上是调用这个函数实现的 Invoke FlushViewOfFile, lpMemory, dwFileSize 。
现在总结一下
使用内存映射文件进行读写文件的步骤:
1.调用 CreatFile 打开想要映射的文件,获得hFile
2.调用 CreatFileMapping 函数生成一个建立在CreatFile函数创建的文件对象基础上的内存映射对象,得到hFileMap
3.调用 MapViewOfFile 函数把整个文件的一个区域或者整个区域映射到内存中,得到指向映射到内存的第一个字节的指针 lpMemory
4.用该指针来读写文件
5.调用 UnmapViewOfFile 来解除文件映射,传入参数为 lpMemory
6.调用 CloseHandle 来关闭内存映射文件,传入参数为 hFileMap
7.调用 CloseHandle 来关闭文件,传入函数为 hFile
使用内存映射文件进行读写文件的步骤:
1.调用 CreatFile 打开想要映射的文件,获得hFile
2.调用 CreatFileMapping 函数生成一个建立在CreatFile函数创建的文件对象基础上的内存映射对象,得到hFileMap
3.调用 MapViewOfFile 函数把整个文件的一个区域或者整个区域映射到内存中,得到指向映射到内存的第一个字节的指针 lpMemory
4.用该指针来读写文件
5.调用 UnmapViewOfFile 来解除文件映射,传入参数为 lpMemory
6.调用 CloseHandle 来关闭内存映射文件,传入参数为 hFileMap
7.调用 CloseHandle 来关闭文件,传入函数为 hFile
例子:
procedure TForm4.Button1Click(Sender: TObject);
var
FileName: string;
FileHandle: THandle;
FileMapHandle: THandle;
Address: PChar;
n: Integer;
begin
FileName := ExtractFilePath(Application.ExeName) + 'gshop.data';
FileHandle := FileOpen(FileName, fmOpenReadWrite);
if FileHandle > 0 then
begin
FileMapHandle := CreateFileMapping(FileHandle, nil, PAGE_READWRITE, 0, 0, 0);
if FileMapHandle > 0 then
begin
Address:= MapViewOfFile(FileMapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if Address <> nil then
begin
n := 10;
Move(n, PByte(Address + 7)^, 1); //写
ShowMessage(IntToStr(PByte(Address + 7)^));
n := 0;
Move(PByte(Address + 7)^, n, 1); //读
ShowMessage(IntToStr(n));
end;
UnmapViewOfFile(Address);
end;
CloseHandle(FileMapHandle);
end;
CloseHandle(FileHandle);
end;
var
FileName: string;
FileHandle: THandle;
FileMapHandle: THandle;
Address: PChar;
n: Integer;
begin
FileName := ExtractFilePath(Application.ExeName) + 'gshop.data';
FileHandle := FileOpen(FileName, fmOpenReadWrite);
if FileHandle > 0 then
begin
FileMapHandle := CreateFileMapping(FileHandle, nil, PAGE_READWRITE, 0, 0, 0);
if FileMapHandle > 0 then
begin
Address:= MapViewOfFile(FileMapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if Address <> nil then
begin
n := 10;
Move(n, PByte(Address + 7)^, 1); //写
ShowMessage(IntToStr(PByte(Address + 7)^));
n := 0;
Move(PByte(Address + 7)^, n, 1); //读
ShowMessage(IntToStr(n));
end;
UnmapViewOfFile(Address);
end;
CloseHandle(FileMapHandle);
end;
CloseHandle(FileHandle);
end;