WIN32汇编语言教程:第10章 内存管理和文件操作 · 10.4 内存映射文件(2)

10.4.2 使用内存映射文件

1. 内存映射文件函数

内存映射文件函数包括:CreateFileMapping,OpenFileMapping,MapViewOfFile,UnmapViewOfFile和FlushViewOfFile。

使用内存映射文件的步骤分为两步,第一步是使用CreateFileMapping创建一个内存映射文件对象。这个步骤决定了使用内存映射文件的用途——究竟是在磁盘文件上建立内存映射文件还是在页文件中建立进程间共享的映射。CreateFileMapping函数的用法是:

   invoke CreateFileMapping,hFile,lpFileMappingAttributes,\
           flProtect,dwMaximumSizeHigh,dwMaximumSizeLow,lpName
   .if    eax
           mov hFileMap,eax
   .endif

函数的第一个参数hFile指定一个文件句柄。如果句柄是属于一个已经打开的文件的,那么内存映射文件将在这个文件上面建立;如果需要建立存在于页文件中的内存映射文件供不同进程共享,那么hFile指定为-1。

lpFileMappingAttributes参数指向一个SECURITY_ATTRIBUTES结构,用来定义内存映射文件对象是否是可继承的。这个结构在文件打开函数中也曾经用到过,如果句柄不需要继承,可以把这个参数设置为NULL。

第三个参数flProtect指定该内存映射文件的保护类型,它可以是以下取值:

● PAGE_READONLY——内存映射文件提交的内存页面是只读的,为了使用此标志获得对应的读权限,在用CreateFile函数打开文件获得hFile句柄时必须相应指定GENERIC_READ标志。

● PAGE_READWRITE——内存映射文件提交的内存是可读写的。为了使用此标志,在用CreateFile函数打开文件获得hFile句柄时,必须同时指定GENERIC_READ标志和GENERIC_WRITE标志。

● PAGE_WRITECOPY——内存映射文件提交的内存可以有Copy on Write属性。为了使用此标志,在用CreateFile函数打开文件获得hFile句柄时,必须同时指定GENERIC_READ标志和GENERIC_WRITE标志。

dwMaximumSizeHigh和dwMaximumSizeLow参数则组合指定了一个64位的内存映射文件的长度。当内存映射文件用于磁盘文件的时候,如果这个长度大于磁盘文件的长度,那么磁盘文件将被扩展到这个长度;如果小于磁盘文件长度,那么只能存取磁盘文件的一部分。一种简单的方法是将这两个参数全部设置为0,那么内存映射文件的大小将被自动调整到磁盘文件的大小。

最后一个参数lpName 指定一个字符串,用来给定内存映射文件的名字。当内存映射文件用于磁盘文件的时候,不需要给它起名;如果用于在进程间共享内存,那么必须为该对象命名,因为在其他进程中只有使用这个名称才能打开这个内存映射文件对象,该名字字符串不能和其他进程已创建的对象同名。

当一个进程创建内存共享文件用于和其他进程共享的时候,其他进程不能再使用CreateFileMapping函数去创建同样的内存映射文件对象,而是要用OpenFileMapping函数去打开已创建好的对象。OpenFileMapping函数的用法是:

invoke OpenFileMapping,dwDesiredAccess,bInheritHandle,lpName
   .if    eax
           mov hFileMap,eax
   .endif

这里的lpName参数指向的名字就是创建对象时使用的名字,dwDesiredAccess参数指定保护类型,它可以是以下的取值:

● FILE_MAP_WRITE(或FILE_MAP_ALL_ACCESS)——可写属性。

● FILE_MAP_READ——可读属性。

● FILE_MAP_COPY——Copy on write属性。

注意:FILE_MAP_ALL_ACCESS等于FILE_MAP_WRITE属性,并不同时包括FILE _MAP_READ属性。如果CreateFileMapping函数或OpenFileMapping函数执行成功,返回的是内存映射文件句柄,这个句柄可以用在后面的函数中,如果执行失败则返回NULL。

使用内存映射文件的第二个步骤是创建内存映射文件的一个视图。获得内存映射文件对象的句柄后,就可以使用它在进程的地址空间中映射该文件的一个视图,该操作可以视为给需要映射的文件内容分配线性地址空间,并将线性地址和文件内容对应起来,这样程序就可以通过存取线性地址来存取文件。

视图可以任意映射或取消映射。当一个文件的视图被映射时,系统仅为它分配足以覆盖文件视图的连续地址空间,并不马上将它提交到当做物理存储器的文件中去,当第一次读写内存页面中任一地址的时候,系统才真正分配一个对应于视图页面的物理内存页面,所以映射视图的速度是相当快的。

MapViewOfFile函数用来映射内存映射文件的一个视图。这个函数的用法是:

invoke MapViewOfFile,hFileMap,dwDesiredAccess,\
           dwFileOffsetHigh,dwFileOffsetLow,dwNumberOfBytesToMap
   .if    eax
           mov    lpMemory,eax
.endif

参数hFileMap就是前两个函数返回的内存映射文件对象的句柄,dwDesiredAccess参数指定保护类型,可能的取值同样是FILE_MAP_WRITE,FILE_MAP_READ或FILE_ MAP_COPY。

一个视图可以映射到整个文件,也可以映射到磁盘文件的一部分。需要映射的起始位置可以由dwFileOffsetHigh和dwFileOffsetLow指定,这两个参数组合成一个64位的偏移量,用来指定视图的基地址是从文件的哪个位置开始映射。dwNumberOfBytesToMap参数指定要映射的字节数,如果dwNumberOfBytesToMap参数设置为0,那么映射的是整个文件,同时偏移地址被忽略。如果映射成功,函数返回一个地址,存取这个地址指定的内存块就相当于存取文件的内容了。如果映射失败,则函数返回NULL。

当不再使用内存映射文件后,可以通过UnmapViewOfFile函数撤销映射并使用CloseHandle函数关闭内存映射文件对象句柄:

   invoke UnmapViewOfFile,lpMemory
   invoke CloseHandle,hFileMap

当对视图中的内存进行修改后,系统会在视图撤销映射或文件映射对象被删除时自动将数据写到磁盘上,但程序也可以根据需要将对文件的修改立即写到磁盘上,该功能是由函数FlushViewOfFile提供的:

   invoke FlushViewOfFile,lpMemory,dwFileSize

该函数将从指定地址开始、指定大小的数据块中的脏页面写到磁盘,指定的内存范围必须位于视图的边界之内。

2. 使用内存映射文件读写文件

通过上一节的讨论,读者已经知道使用内存映射文件读写文件的步骤为:

(1)调用CreateFile打开想要映射的文件,得到hFile。

(2)调用CreateFileMapping函数生成一个建立在CreateFile函数创建的文件对象基础上的内存映射对象,得到hFileMap。

(3)调用MapViewOfFile函数把整个文件的一个区域或者整个文件映射到内存中。得到指向映射到内存的第一个字节的指针lpMemory。

(4)用该指针来读写文件。

(5)调用UnmapViewOfFile来解除文件映射,传入参数为lpMemory。

(6)调用CloseHandle来关闭内存映射文件,传入参数为hFileMap。

(7)调用CloseHandle来关闭文件,传入参数为hFile。

现在,将WordCount例子中的读文件部分改成使用内存映射文件的方法,将其中的_WordCount子程序改成下面的样子后,使用效果是一样的:

上页:第10章 内存管理和文件操作 · 10.4 内存映射文件(1) 下页:第10章 内存管理和文件操作 · 10.4 内存映射文件(3)

第10章 内存管理和文件操作

版权所有 © 中山市飞娥软件工作室 证书:粤ICP备09170368号