在Windows程序开发过程中,当多个进程之间需要使用同样的数据的时候我们最好的方式就是通过共享内存进行处理(比如:当A进程运行时,进行数据处理,那么此时我想知道数据是不是正确,用B监控,那么A与B之间就可以使用共享内存的方式,并且这时来个C进程,他也可以访问这块共享内存数据进行监控)。
1.共享内存的原理
如图所示,尽管进程A和进程B在Windows下有两个相互独立的逻辑地址,但是在数据上,他们共用同一块内存区域,使得他们的数据保持一致。
2.Windows下的创建共享内存CreateFileMapping
MSDN中关于CreateFileMapping
的定义
HANDLE WINAPI CreateFileMapping(
_In_ HANDLE hFile,
_In_opt_ LPSECURITY_ATTRIBUTES lpAttributes,
_In_ DWORD flProtect,
_In_ DWORD dwMaximumSizeHigh,
_In_ DWORD dwMaximumSizeLow,
_In_opt_ LPCTSTR lpName
);
参数解释:
hFile
用于创建文件映射对象的文件的句柄。一般情况我们将这个参数设置为INVALID_HANDLE_VALUE
,如果hFile设为INVALID_HANDLE_VALUE
,调用进程还必须在dwMaximumSizeHigh
和 dwMaximumSizeLow
参数中指定文件映射对象的大小。
lpAttributes
保护设置或者称为安全设置,我们一般设置为NULL,这样就是windows的默认安全设置。
flProtect
访问权限设置,通常有PAGE_READONLY
、PAGE_READWRITE
和PAGE_WRITECOPY
,释义分别为只读,可读写,写时复制访问(留下备份)。
dwMaximumSizeHigh和dwMaximumSizeLow
高低位文件大小,当flProtect 设置为只读时,这样我们并不改变其大小,所以可以将这两个参数设置为0。否则我们将dwMaximumSizeHigh
设置为0,dwMaximumSizeLow
设置为我们想开辟的内存字节数。
lpName
共享文件内存的名称,一般比如我们开辟时设置为ShareMemoryTest,当我们访问时则也是访问这个名称ShareMemoryTest
3.将共享内存映射到进程的地址空间MapViewOfFile
MSDN中关于MapViewOfFile
的定义
LPVOID WINAPI MapViewOfFile(
_In_ HANDLE hFileMappingObject,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwFileOffsetHigh,
_In_ DWORD dwFileOffsetLow,
_In_ SIZE_T dwNumberOfBytesToMap
);
参数解释:
hFileMappingObject
文件映射对象的句柄。接收CreateFileMapping
和 OpenFileMapping
函数返回该句柄。
dwDesiredAccess
访问文件映射对象的类型(权限)。要与在CreateFileMapping()
时设置的访问权限相匹配。FILE_MAP_ALL_ACCESS
等价于CreateFileMapping
的FILE_MAP_WRITE|FILE_MAP_READ
. 文件映射对象被创建时必须指定PAGE_READWRITE
选项.
FILE_MAP_COPY
可以读取和写入文件.写入操作会导致系统为该页面创建一份副本.在调用CreateFileMapping
时必须传入PAGE_WRITECOPY
保护属性
dwFileOffsetHigh和dwFileOffsetLow
文件映射起始偏移的高32位和低32位,通常情况都设置为0。
dwNumberOfBytesToMap
指定需要映射的文件的字节数量,当dwNumberOfBytesToMap
为0,则映射的是整个文件
返回值:当文件成功映射时则返回的值是映射到进程地址空间的首地址
4.打开共享内存OpenFileMapping()
MSDN中关于OpenFileMapping
的定义
HANDLE WINAPI OpenFileMapping(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ LPCTSTR lpName
);
参数释义
dwDesiredAccess
和MapViewOfFile
中的dwDesiredAccess
释义一样。
bInheritHandle
如果此参数为TRUE,CreateProcess
函数创建的进程 可以继承该句柄; 否则,句柄不能被继承,一般情况下我们设置为FALSE。
lpName
要打开的文件映射对象的名称,也就是CreateFileMapping
时的lpName。
5.代码示例
写端
#include
#include
#include
#pragma warning(disable:4996)
using namespace std;
int main()
{
string strMapName("ShareMemory");// 内存映射对象名称
string strData;//用于存储写入数据
LPVOID pBuffer;// 共享内存指针
HANDLE hMap;//定义一个句柄
getline(cin,strData);//读取一行数据给strData
hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,0,
strData.size(),
(LPCWSTR)strMapName.c_str());
pBuffer = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);//得到与共享内存映射的指针
strcpy((char*)pBuffer, strData.c_str());//写入数据
cout << "写入共享内存数据:" << (char *)pBuffer << endl;
system("pause");
::UnmapViewOfFile(pBuffer);//停止指针到共享内存的映射
::CloseHandle(hMap);//关闭共享内存的句柄
return 0;
}
读端
#include
#include
#include
#pragma warning(disable:4996)
using namespace std;
int main()
{
string strMapName("ShareMemory");// 内存映射对象名称
string strData;//用于存储共享内存中的数据
LPVOID pBuffer=NULL;// 共享内存指针
HANDLE hMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, (LPCWSTR)strMapName.c_str());// 先判断要打开的共享内存名称是否存在
if (NULL == hMap)
{
cout << "尚未创建共享内存" << endl;
}
else
{ //共享内存存在,获得指向共享内存的指针,显示出数据
pBuffer = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
cout << "读取出共享内存数据:" << (char *)pBuffer << endl;
}
system("pause");
::UnmapViewOfFile(pBuffer);
::CloseHandle(hMap);
return 0;
}