内存映射文件技术

1. 用途和基本操作
用于不同进程之间的内存共享操作, 可以将一个物理文件映射到内存当中然后直接利用分配到的或者打开的命名共享内存的地址空间实现资源共享访问

2. 相关流程
1) 新建命名共享内存
首先利用CreateFile或者CreateFileForMapping获得一个用于映射的物理文件句柄, 然后利用该文件句柄结合CreateFileMapping得到一个命名的共享内存映射文件句柄。
//CreateFileMapping
为指定文件创建一个有名或无名的文件映象;
HANDLE CreateFileMapping(
  HANDLE hFile,              //
映射文件的句柄
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes, //
安全描述符指针
  DWORD flProtect,           //
对映射对象的保护
  DWORD dwMaximumSizeHigh,   //
对象最大长度的高32位
  DWORD dwMaximumSizeLow,    //
对象最大长度的低32位
  LPCTSTR lpName             //
文件内存映射对象的名字
);

注意:
hFile
: 映射文件的句柄,文件的打开模式必须与flProtect参数指定的相一致;如果这个参数值为0xFFFFFFFF,那么必须在 dwMaximumSizeHigh和dwMaximumSizeLow参数中指定映射对象的大小。并且将在操作系统虚拟内存页面替换文件中创建文件映射 对象,而不是使用磁盘文件,同时必须给出这个映射对象的大小。文件映射对象通过副本,遗传或名字来共享。
lpFileMappingAttributes
:安全描述符指针,决定返回句柄是否能被子进程继承,如果是NULL,那么子进程不能继承。WinNt中,如果是NULL,那么文件映射对象得到一个默认的安全描述符。
flProtect
:为得到的文件试图指定保护模式,可以被设置为下列值:
 PAGE_READONLY
:只读属性,并且hFile对应的文件必须以GENERIC_READ形式打开。
 PAGE_READWRITE
:可读可写属性,并且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。
 PAGE_WRITECOPY
:对可写区域复制后操作,并且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。
dwMaximumSizeHigh
,dwMaximumSizeLow:如果这两个参数为0,则文件映射对象的最大长度等于hFile指定的文件长度。
lpName
: 文件映射对象的名字,如果这个名字已存在,则按照flProtect指定的来处理映射对象。如果此参数为空,则创建一个无名字的文件映射对象。如果此参数 的名字与系统事件的名字相同,则函数执行失败,GetLastError返回 ERROR_INVALID_HANDLE

返回值:函数调用成功返回文件映射对象的句柄,如果文件映射对象已经存在则返回原有映射对象的句柄,GetLastError返回ERROR_ALREADY_EXISTS。函数执行失败返回Null。

2) 打开命名共享内存
如果需要共享已经存在的命名共享内存映射文件, 使用OpenFileMapping函数。
//OpenFileMapping
打开一个已命名的文件映射对象
HANDLE OpenFileMapping(
  DWORD dwDesiredAccess,  //
访问模式
  BOOL bInheritHandle,    //
继承标志
  LPCTSTR lpName          //
文件映射对象名指针
);
注意:
dwDesiredAccess
:访问模式与MapViewOfFile中的访问模式相同。
bInheritHandle
:继承标志,是否可以被一个新的进程继承使用,如果为TRUE,就可以被一个新进程继承句柄。
返回值:
 
成功返回一个已命名的文件映射对象,失败返回NULL。

3) 获得地址空间指针
进行内存映射文件的读写和一般的文件读写不同, 是直接面对你申请的地址空间, 为此需要使用MapViewOfFile得到相关的地址LPVOID类型的指针。如果需要进行文件写入, 可以通过类型转换直接对于内存地址进行赋值, 比如:
memcpy( lpAddress, lpBuf, ....)
这里自然需要防止内存溢出的情况。
如果是读取操作,将参数顺序调整一下就可以了。

MapViewOfFile
在调用进程的地址空间映射一个文件视图
LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,  //
已创建的文件映射对象句柄
  DWORD dwDesiredAccess,      //
访问模式
  DWORD dwFileOffsetHigh,     //
文件偏移的高32位
  DWORD dwFileOffsetLow,      //
文件偏移的低32位
  DWORD dwNumberOfBytesToMap  //
映射视图的大小
);
注意:
hFileMappingObject
: 由CreateFileMapping 或 OpenFileMapping 返回的文件映射对象句柄。
dwDesiredAccess
:映射视图的访问模式,与创建文件映射对象的保护模式flProtect有关,可以被设置为下列值:
 FILE_MAP_WRITE
:一个可读写属性的文件视图被创建,保护模式为PAGE_READWRITE
 FILE_MAP_READ
:一个只读属性的文件视图被创建,保护模式为PAGE_READWRITE PAGE_READONLY
 FILE_MAP_ALL_ACCESS
:与FILE_MAP_WRITE模式相同
 FILE_MAP_COPY
:保护模式为PAGE_WRITECOPY时,得到一个视图文件,当你对视图文件写操作时,页面自动交换,并且你所做的修改不会损坏原始数据资料。
dwNumberOfBytesToMap
:映射文件部分的大小,如果为0,则映射整个文件。
返回值:
如果成功返回返回映射视图的起始地址,如果失败返回NULL。

4)MapViewOfFileEx 在调用进程的地址空间映射一个文件视图,并且允许调用进程为映射视图指定特殊的内存地址 
LPVOID MapViewOfFileEx(
  HANDLE hFileMappingObject,  //
文件映射对象的句柄
  DWORD dwDesiredAccess,      //
访问模式
  DWORD dwFileOffsetHigh,     //
文件偏移的高32位
  DWORD dwFileOffsetLow,      //
文件偏移的低32位
  DWORD dwNumberOfBytesToMap, //
映射视图的大小
  LPVOID lpBaseAddress        //
指定映射视图的其实内存地址
);
注意:
与MapViewOfFile用法相同,但是如果指定的内存地址空间大小不够,则函数执行失败。

5) 将内存复制到所映射的物理文件上面
FlushMapViewOfFile函数可以将内存里面的内容DUMP到物理磁盘上面
FlushViewOfFile
把文件映射视图中的修改的内容或全部写回到磁盘文件中
BOOL FlushViewOfFile(
  LPCVOID lpBaseAddress,       //
修改内容的起始地址
  DWORD dwNumberOfBytesToFlush //
修改的字节数目
);
函数执行成功返回非零。

6) 卸载内存映射文件地址指针
UnmapViewOffFile函数就是卸载
UnmapViewOfFile
删除文件的映射视图
BOOL UnmapViewOfFile(
  LPCVOID lpBaseAddress   //
映射视图起始地址
);
注意:
lpBaseAddress
:映射视图起始地址,由 MapViewOfFile 函数 MapViewOfFileEx产生。
返回值:
如果调用成功返回非零,并且所有指定地址内的脏页面会被写入硬盘。调用失败返回零。

7) 关闭内存映射文件
太简单了, CloseHandle搞定

原文地址:https://www.cnblogs.com/loongfee/p/2268722.html