本文通过内存映射文件的使用来实现对大文件的访问,同时介绍了内存内存映射文件的相关概念 .
下面分别对这些关键函数进行说明:
1)CreateFile():CreateFile()函数是一个用途非常广泛的函数, 在这里的用法并没有什么特殊的地方,但有几点需要注意:一是访问模式参数dwDesiredAccess。该参数设置了对文件内核对象的访问类型,其允许设置的权限可以为读权限GENERIC_READ、写权限GENERIC_WRITE、读写权限GENERIC_READ | GENERIC_WRITE和设备查询权限0。在使用映射文件时,只能打开那些具有可读访问权限的文件,即只能应用GENERIC_READ和GENERIC_READ | GENERIC_WRITE这两种组合;另一点需要注意的是共享模式参数dwShareMode。该参数定义了对文件内核对象的共享方式,其可能的设置为FILE_SHARE_READ、FILE_SHARE_WRITE和0,并可对其组合使用。其中,设置为0时不允许共享对象;FILE_SHARE_READ和FILE_SHARE_WRITE分别为在要求只读、只写访问的情况下才允许对象的共享。
由于通过内存映射文件可以在多个进程间共享数据,因此在进行这种应用时应当考虑dwShareMode参数设置对运行结果的影响。
2)CreateFileMapping():该函数的作用是创建一个文件映射内核对象,以告知系统文件映射对象需要多大的物理存储器。创建内存映射文件对象对系统资源几乎没有什么影响,也不会影响进程的虚拟地址空间。除了需要用来表示该对象的内部资源之外通常并不用为其分配虚拟内存,但是如果内存映射文件对象是作共享内存之用的话,就要在创建对象时由系统为内存映射文件的使用在系统页文件中保留足够的空间。
函数第一个参数hFile为标识要映射到进程的地址空间的文件的句柄。虽然由于内存映射文件的物理存储器是来自于磁盘上的文件,而非系统的页文件,使创建内存映射文件就像保留一个地址空间区域并将物理存储器提交给该区域一样。第二个参数为指向文件映射内核对象的SECURITY_ATTRIBUTES结构的指针,由此来决定子进程能否继承得到返回的句柄。通常为其传递NULL值,以默认的安全属性来禁止返回句柄的被继承。
接下来的参数用于文件被映射后设定文件映像的保护属性。其可能的取值为PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY。虽然在创建文件映射对象时,系统并不为其保留地址空间区域,也不将文件的存储器映射到该区域。但是,在系统将存储器映射到进程的地址空间中去时,系统必须确切知道应赋予物理存储器页面的保护属性。在设置保护属性时,必须与用CreateFile()函数打开文件时所指定的访问标识相匹配,否则将导致CreateFileMapping()的执行失败。因此这里设置PAGE_READWRITE属性。除了上述三个页面保护属性外,还有4个区(Section)保护属性也可以一起组合使用:
区保护属性 |
说明 |
SEC_COMMIT |
为区中的所有页面在内存中或磁盘页面文件中分配物理存储器 |
SEC_IMAGE |
告知系统,映射的文件是一个可移植的EXE文件映像 |
SEC_NOCACHE |
告知系统,未将文件的任何内存映射文件放入高速缓存,多供硬件设备驱动程序开发人员使用 |
SEC_RESERVE |
对一个区的所有页面进行保留而不分配物理存储器 |
后面的两个参数指定了要创建的文件映射对象的最大字节数的高32位值和低32位值,实际也就设定了文件的最大字节数(最大可以处理16EB的文件)。这两个参数可以满足确保文件映射对象能够得到足够的物理存储器这一基本条件。在参数设置的大小小于文件实际大小时,系统将从文件映射指定的字节数。这里将其设置为0,将使所创建的文件映射对象将为文件的当前大小,以上两种情况均无法改变文件的大小。如果设置的参数大于文件的实际大小,系统将会在CreateFileMapping()函数返回前扩展该文件。需要指出的是,文件映射对象的大小是静态的,一旦创建完毕后将无法更改。如果设置的文件映射对象尺寸偏小将导致无法对文件进行全面的访问。
在本节开始也曾提到过,创建文件映射对象是不需要花费什么系统资源的,因此遵循"宁多勿缺"的原则,一般应将文件映射对象的大小设置为文件大小的相同值。函数最后的参数将可以为映射对象命名。如果想打开一个已存在的文件映射对象,该对象必须要命名。对该名字字符串的要求仅限于未被其它对象使用过的名字即可。
CreateFileMapping()在成功执行后将返回一个指向文件映射对象的句柄。如果对一个已经存在的文件映射对象调用了CreateFileMapping()函数,进程将得到一个指向现有映射对象的句柄。通过调用GetLastError()可以得到返回值ERROR_ALREADY_EXIST,由此可以判断当前得到的内存映射对象句柄是新创建的还是打开已经存在的。如果系统无法创建文件映射对象,将导致CreateFileMapping()的执行失败,返回N U L L句柄值。