通过CreateFileMapping实现内存共享

转载:https://www.cnblogs.com/hrhguanli/p/4007100.html

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搞定

上一篇:python中出现 IndentationError:unindent does not match any outer indentation level


下一篇:【C# 线程】Windows系统下常见的7种I/O模型 之Overlapped I/O模型