文章来源:http://blog.csdn.net/huanglong8/article/details/53954601
Windows提供了3种进行内存管理的方法:
- 虚拟内存,最适合用来管理大型对象或结构数组。
- 内存映射文件,最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行的多个进程之间共享数据。
- 内存堆栈,最适合用来管理大量的小对象。
跨进程共享数据的一种方式就是使用内存文件映射。
我的理解是,在内存中申请一片内存区域,这片内存就像将硬盘上的文件预先加载进去一样,这样各个进程间访问的时候,访问的就是同一片区域了,从而达到内存共享的目的。
WINAPI在这里给我们提供了同共享内存类似的函数,仅仅只有创建销毁这一部分会有不同。
创建文件映射内核对象
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:Long,指定欲在其中创建映射的一个文件句柄。0xFFFFFFFF(-1,即INVALID_HANDLE_VALUE)表示在页面文件中创建一个可共享的文件映射。这个参数通常被文件系统默认的可创建文件大小来限定,根据系统的不同,创建的内存文件大小的限值也不同。
lpFileMappigAttributes:SECURITY_ATTRIBUTES,它指明返回的句柄是否可以被子进程所继承,指定一个安全对象,在创建文件映射时使用。如果为NULL(用ByVal As Long传递零),表示使用默认安全对象。其中里面包含了安全描述符。
包含和被保护对象相关联的安全信息的数据结构。安全描述符包括谁拥有对象,以何种方式访问以及何种审查访问类型等信息。
flProtect:Long,下述常数之一:
PAGE_READONLY 以只读方式打开映射
PAGE_READWRITE 以可读、可写方式打开映射
PAGE_WRITECOPY 为写操作留下备份
可组合使用下述一个或多个常数:
SEC_COMMIT 为文件映射一个小节中的所有页分配内存
SEC_IMAGE 文件是个可执行文件
SEC_RESERVE 为没有分配实际内存的一个小节保留虚拟内存空间
dwMaximumSizeHigh:Long,文件映射的最大长度的高32位。
dwMaximumSizeLow:Long,文件映射的最大长度的低32位。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度。
lpName:String,指定文件映射对象的名字。如存在这个名字的一个映射,函数就会打开它。用vbNullString可以创建一个无名的文件映射。
调用CreateFileMapping的时候可能会出现的GetLastError的相应错误:
ERROR_FILE_INVALID 如果企图创建一个零长度的文件映射
ERROR_INVALID_HANDLE 内存空间的命名和现有的内存映射,互斥量,信号量,临界区有同名
ERROR_ALREADY_EXISTS 表示内存空间命名已经存在
在调用CreateFileMapping()时,可以用GetLastError()来检查其返回的错误信息。如果返回值为ERROR_ALREADY_EXISTS,则表示内存映射对象指定名字已经存在。有关其他返回值的意义见MSDN的详细说明。
映射内存创建好后需要调用WINAPI进行打开,并且如果有其他程序创建了映射,则直接用 OpenFileMapping 函数进行打开,从而实现两个进程间的通信。
HANDLE
OpenFileMappingA(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ LPCSTR lpName
);
参数的意义同CreateFileMapping一样。
创建了内存文件后,则是很简单的WriteFile和ReadFile函数对文件内容进行读写操作。其中需要注意的是,MapViewOfFile 我们需要通过这个API函数来获取char* 类型的buff。
LPVOID WINAPI MapViewOfFile(
__in HANDLE hFileMappingObject,
__in DWORD dwDesiredAccess,
__in DWORD dwFileOffsetHigh,
__in DWORD dwFileOffsetLow,
__in SIZE_T dwNumberOfBytesToMap
);
hFileMappingObject 为CreateFileMapping()返回的文件映像对象句柄。
dwDesiredAccess 映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。 可取以下值:
FILE_MAP_ALL_ACCESS 等价于CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ. 文件映射对象被创建时必须指定PAGE_READWRITE 选项.
FILE_MAP_COPY 可以读取和写入文件.写入操作会导致系统为该页面创建一份副本.在调用CreateFileMapping时必须传入PAGE_WRITECOPY保护属性.
FILE_MAP_EXECUTE 可以将文件中的数据作为代码来执行.在调用CreateFileMapping时可以传入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保护属性.
FILE_MAP_READ 可以读取文件.在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性.
FILE_MAP_WRITE 可以读取和写入文件.在调用CreateFileMapping时必须传入PAGE_READWRITE保护属性.
dwFileOffsetHigh 表示文件映射起始偏移的高32位.
dwFileOffsetLow 表示文件映射起始偏移的低32位.(64KB对齐不是必须的)
dwNumberOfBytes 指定映射文件的字节数.
以上的参数较多,但如果没有特定需求的话,都可以指定默认值。
我们开始完成 团灭笔记 的示例。
首先写一个打开文件映射的函数,当没有时则创建再打开,当存在时则直接打开。
A进程源代码
#include <windows.h>
#include <stdio.h>
#define SENDMMFShare "SENDMMFShare"
#define RECVMMFShare "RECVMMFShare"
HANDLE hSendThread,hRecvThread;
LPDWORD hSendThreadID,hRecvThreadID;
HANDLE OpenOrCreateFileMapping(DWORD maxsize,LPCSTR lpname )
{
HANDLE fileMap = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,FALSE, lpname);
if (!fileMap)
{
fileMap = CreateFileMapping(INVALID_HANDLE_VALUE,NULL, PAGE_READWRITE, 0, maxsize,lpname);
}
return fileMap;
}
DWORD WINAPI Send(LPVOID lpParameter )
{
char buff[0xFFF];
ZeroMemory(buff,sizeof(buff));
HANDLE s_hFileMap = NULL;
char* pBuff = NULL;
s_hFileMap = OpenOrCreateFileMapping(1024,SENDMMFShare);
if(!s_hFileMap)
{
printf("Can't create file mapping.\n");
return 0;
}
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
printf("Mapping already exists - not created.\n");
CloseHandle(s_hFileMap);
return 0;
}
pBuff = (char*)MapViewOfFile(s_hFileMap,FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0xFFF);
if(!pBuff)
{
printf("Can't map view of file.");
CloseHandle(s_hFileMap);
return 0;
}
while(true)
{
gets_s(buff,sizeof(buff));
strncpy(pBuff,buff,0xFFF);
}
CloseHandle(s_hFileMap);
UnmapViewOfFile(pBuff);
return 0;
}
DWORD WINAPI Recv(LPVOID lpParameter )
{
char buffer[2048] = {0};
HANDLE hFileMapT = OpenOrCreateFileMapping(1024,RECVMMFShare);
if (!hFileMapT)
{
printf("Can't open mapping.");
return 0;
}
char* pBuff = (char*)MapViewOfFile(hFileMapT, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
if (!pBuff)
{
printf("Can't map view.");
CloseHandle(hFileMapT);
return 0;
}
while(1)
{
if(strlen(pBuff)>0)
{
printf("From B data: %s\n",pBuff);
ZeroMemory(pBuff,1024);
}
Sleep(200);
}
UnmapViewOfFile(pBuff);
CloseHandle(hFileMapT);
}
int main(int argc, char *argv[])
{
hSendThread = CreateThread(NULL,0,Send,NULL,NULL,hSendThreadID);
hRecvThread = CreateThread(NULL,0,Recv,NULL,NULL,hRecvThreadID);
if( WaitForSingleObject(hSendThread,INFINITE))
CloseHandle(hSendThread);
if( WaitForSingleObject(hRecvThread,INFINITE))
CloseHandle(hRecvThread);
return 0;
}
B进程源代码
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#define SENDMMFShare "RECVMMFShare"
#define RECVMMFShare "SENDMMFShare"
HANDLE hSendThread,hRecvThread;
LPDWORD hSendThreadID,hRecvThreadID;
HANDLE OpenOrCreateFileMapping(DWORD maxsize,LPCSTR lpname )
{
HANDLE fileMap = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,FALSE, lpname);
if (!fileMap)
{
fileMap = CreateFileMapping(INVALID_HANDLE_VALUE,NULL, PAGE_READWRITE, 0, maxsize,lpname);
}
return fileMap;
}
DWORD WINAPI Send(LPVOID lpParameter )
{
char buff[0xFFF];
ZeroMemory(buff,sizeof(buff));
HANDLE s_hFileMap = NULL;
char* pBuff = NULL;
s_hFileMap = OpenOrCreateFileMapping(1024,SENDMMFShare);
if(!s_hFileMap)
{
printf("Can't create file mapping.\n");
return 0;
}
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
printf("Mapping already exists - not created.\n");
CloseHandle(s_hFileMap);
return 0;
}
pBuff = (char*)MapViewOfFile(s_hFileMap,FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0xFFF);
if(!pBuff)
{
printf("Can't map view of file.");
CloseHandle(s_hFileMap);
return 0;
}
while(true)
{
gets_s(buff,sizeof(buff));
strncpy(pBuff,buff,0xFFF);
}
CloseHandle(s_hFileMap);
UnmapViewOfFile(pBuff);
return 0;
}
DWORD WINAPI Recv(LPVOID lpParameter )
{
char buffer[2048] = {0};
HANDLE hFileMapT = OpenOrCreateFileMapping(1024,RECVMMFShare);
if (!hFileMapT)
{
printf("Can't open mapping.");
return 0;
}
char* pBuff = (char*)MapViewOfFile(hFileMapT, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
if (!pBuff)
{
printf("Can't map view.");
CloseHandle(hFileMapT);
return 0;
}
while(1)
{
if(strlen(pBuff)>0)
{
printf("From A data: %s\n",pBuff);
ZeroMemory(pBuff,1024);
}
Sleep(200);
}
UnmapViewOfFile(pBuff);
CloseHandle(hFileMapT);
}
int main(int argc, char *argv[])
{
hSendThread = CreateThread(NULL,0,Send,NULL,NULL,hSendThreadID);
hRecvThread = CreateThread(NULL,0,Recv,NULL,NULL,hRecvThreadID);
if( WaitForSingleObject(hSendThread,INFINITE))
CloseHandle(hSendThread);
if( WaitForSingleObject(hRecvThread,INFINITE))
CloseHandle(hRecvThread);
return 0;
}
参考:
http://www.cnblogs.com/fangyukuan/archive/2010/09/09/1822216.html