Windows使用内存映射文件

1.简介

内存映射文件主要用于以下三种情况:

  • 系统使用内存映射文件,以便加载和执行. exe和DLL文件。这可以大大节省页文件空间和应用程序启动运行所需的时间。

  • 可以使用内存映射文件来访问磁盘上的数据文件。这使你可以不必对文件执行I/O操作,并且可以不必对文件内容进行缓存。

  • 可以使用内存映射文件,使同一台计算机上运行的多个进程能够相互之间共享数据。Windows确实提供了其他一些方法,以便在进程之间进行数据通信,但是这些方法都是使用内存映射文件来实现的,这使得内存映射文件成为单个计算机上的多个进程互相进行通信的最有效的方法。

使用内存映射文件,需要执行下面三个步骤。

  1. 创建或打开一个文件内核对象,该对象标识了我们想要用作内存映射文件的那个磁盘文件。

  2. 创建一个文件映射内核对象,告诉系统该文件的大小和你打算如何访问该文件。

  3. 告诉系统把文件映射对象的部分或全部映射到进程的地址空间中。

当完成对内存映射文件的使用时,必须执行下面这些步骤将它清除:

  1. 告诉系统从你的进程的地址空间中撤消文件映射内核对象的映射。
  2. 关闭文件映射内核对象。
  3. 关闭文件内核对象。

2.示例

步骤1:创建或打开文件内核对象

调用CreateFile来创建或打开一个文件内核对象。

HANDLE CreateFile(
   PCSTR pszFileName,
   DWORD dwDesiredAccess,
   DWORD dwShareMode,
   PSECURITY_ATTRIBUTES psa,
   DWORD dwCreationDisposition,
   DWORD dwFlagsAndAttributes,
   HANDLE hTemplateFile
);

步骤2:创建文件映射内核对象

调用CreateFileMapping函数告诉系统,文件映射内核对象需要多少物理存储器。

HANDLE CreateFileMapping(
   HANDLE hFile,
   PSECURITY_ATTRIBUTES psa,
   DWORD fdwProtect,
   DWORD dwMaximumSizeHigh,
   DWORD dwMaximumSizeLow,
   PCTSTR pszName
);
  • hFile:用于标识你想要映射到进程地址空间中的文件句柄。该句柄由前面调用的CreateFile函数返回。
  • psa:参数是指向文件映射内核对象的SECURITY_ATTRIBUTES结构的指针。
  • fdwProtect:使你能够设定这些保护属性。大多数情况下,可以设定下面列出的3个保护属性之一。
  • 第四和五个参数:dwMaximumSizeHigh和dwMaximumSizeLow这两个参数将告诉系统该文件的最大字节数。
  • pszName:该名字用于与其他进程共享文件映射对象。

保护属性:

  • PAGE_READONLY:当文件映射对象被映射时,可以读取文件的数据。必须已经将GENERIC_READ传递给CreateFile函数
  • PAGE_READWRITE:当文件映射对象被映射时,可以读取和写入文件的数据。必须已经将GENERIC_READ | GENERIC_WRITE传递给CreateFile
  • PAGE_WRITECOPY:当文件映射对象被映射时,可以读取和写入文件的数据。如果写入数据,会导致页面的私有拷贝得以创建。必须已经将GENERIC_READ或GENERIC_WRITE传递给CreateFile。

步骤3:将文件的数据映射到进程的地址空间

将文件的数据作为映射到该区域的物理存储器进行提交。

PVOID MapViewOfFile(
   HANDLE hFileMappingObject,
   DWORD dwDesiredAccess,
   DWORD dwFileOffsetHigh,
   DWORD dwFileOffsetLow,
   SIZE_T dwNumberOfBytesToMap
);
  • hFileMappingObject:于标识文件映射对象的句柄,该句柄是前面调用CreateFileMapping或OpenFileMapping函数返回的。
  • dwDesiredAccess:标识如何访问该数据。
  • 第三四个参数:dwFileOfsetHigh和dwFileOfsetLow参数。指定哪个字节应该作为视图中的第一个字节来映射。
  • dwNumberOfBytesToMap:有多少字节要映射到地址空间。如果设定的值是0,那么系统将设法把从文件中的指定位移开始到整个文件的结尾的视图映射到地址空间。

步骤4:从进程的地址空间中撤消文件数据的映像

BOOL UnmapViewOfFile(PVOID pvBaseAddress);
  • pvBaseAddress由MapViewOfFile函数返回。

如果没有调用这个函数,那么在进程终止运行前,保留的区域就不会被释放。每当调用MapViewOfFile时,系统总是在你的进程地址空间中保留一个新区域,而以前保留的所有区域将不被释放。

为了提高速度,系统将文件数据的页面进行缓存处理,这样在处理文件映射视图的时候就不需要随时更新磁盘上的文件。如果需要确保你的更新被写入磁盘,可以强制系统将修改过的数据的一部分或全部重新写入磁盘映像中,方法是调用FlushViewOfFile函数:

BOOL FlushViewOfFile(
   PVOID pvAddress,
   SIZE_T dwNumberOfBytesToFlush
);
  • 第一个参数是包含在内存映射文件中的视图的一个字节的地址。该函数会把传入的地址向下取整到页面大小的整数倍。
  • 第二个参数用于指明你想要刷新的字节数。系统将把这个数字向上取整,使得总字节数是页面大小的整数倍。如果你调用FlushViewOfFile函数并且不修改任何数据,那么该函数只是返回,而不将任何信息写入磁盘。

步骤5:关闭文件映射对象和文件对象

HANDLE hFile = CreateFile(...);

HANDLE hFileMapping = CreateFileMapping(hFile, ...);

CloseHandle(hFile);

PVOID pvFile = MapViewOfFile(hFileMapping, ...);

CloseHandle(hFileMapping);

// Use the memory-mapped file.

UnmapViewOfFile(pvFile);

源码:

#include <iostream>
#include <windows.h>
#include <iostream>
using namespace std;

int main()
{
	// 自己创建一个test.txt文件,并写入内容
	HANDLE hFile = CreateFile(L"D:\\test.txt",GENERIC_READ | GENERIC_WRITE,
		0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

	// Create a file-mapping object for the file.
	HANDLE hFileMapping = CreateFileMapping(hFile,
	NULL,
	PAGE_WRITECOPY,
	0, 0,
	NULL);

	PBYTE pbFile = (PBYTE)MapViewOfFile(hFileMapping, FILE_MAP_COPY, 0, 0, 0);

	cout << pbFile << endl;

	UnmapViewOfFile(pbFile);
	CloseHandle(hFileMapping);
	CloseHandle(hFile);

	return 0;
}

猜你喜欢

转载自blog.csdn.net/wzz953200463/article/details/127415364
今日推荐