0、共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信。因为是通过内存操作实现通信,因此是一种最高效的数据交换方法。
调用 CreateFileMapping 创建一个内存文件映射对象:
HANDLE CreateFileMapping(
HANDLE hFile, // handle to file to map
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
// optional security attributes
DWORD flProtect, // protection for mapping object
DWORD dwMaximumSizeHigh, // high-order 32 bits of object size
DWORD dwMaximumSizeLow, // low-order 32 bits of object size
LPCTSTR lpName // name of file-mapping object
);
通过这个API函数 将创建一个内存映射文件的内核对象,用于映射文件到内存。与虚拟内存一样,内存映射文件可以用来保留一个地址空间的区域,并将物理存储器提交
hFile:用于标识你想要映射到进程地址空间中的文件句柄。该句柄可以通过调用C r e a t e F i l e函数返回。这里,我们并不需要一个实际的文件,所以,就不需要调用 CreateFile 创建一个文件, hFile 这个参数可以填写 INVALID_HANDLE_VALUE;
lpFileMappingAttributes:参数是指向文件映射内核对象的 SECURITY_ATTRIBUTES结构的指针,通常传递的值是 N U L L;
flProtect:对内存映射文件的安全设置(PAGE_READONLY 以只读方式打开映射;PAGE_READWRITE 以可读、可写方式打开映射;PAGE_WRITECOPY 为写操作留下备份)
dwMaximumSizeHigh:文件映射的最大长度的高32位。
dwMaximumSizeLow:文件映射的最大长度的低32位。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度。
lpName:指定文件映射对象的名字,别的进程就可以用这个名字去调用 OpenFileMapping 来打开这个 FileMapping 对象。
如果创建成功,返回创建的内存映射文件的句柄,如果已经存在,则也返回其句柄,但是调用 GetLastError()返回的错误码是:183(ERROR_ALREADY_EXISTS),如果创建失败,则返回NULL;
调用 MapViewOfFile 映射到当前进程的虚拟地址上:
如果调用CreateFileMapping成功,则调用MapViewOfFile函数,将内存映射文件映射到进程的虚拟地址中。
LPVOID MapViewOfFile(
HANDLE hFileMappingObject, // file-mapping object to map into
// address space
DWORD dwDesiredAccess, // access mode
DWORD dwFileOffsetHigh, // high-order 32 bits of file offset
DWORD dwFileOffsetLow, // low-order 32 bits of file offset
DWORD dwNumberOfBytesToMap // number of bytes to map
);
hFileMappingObject:CreateFileMapping()返回的文件映像对象句柄。
dwDesiredAccess: 映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。
dwFileOffsetHigh: 表示文件映射起始偏移的高32位.
dwFileOffsetLow: 表示文件映射起始偏移的低32位.
dwNumberOfBytesToMap :文件中要映射的字节数。为0表示映射整个文件映射对象。
在接收进程中打开对应的内存映射对象:
在数据接收进程中,首先调用OpenFileMapping()函数打开一个命名的文件映射内核对象,得到相应的文件映射内核对象句柄hFileMapping;如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,将文件映射内核对象hFileMapping映射到当前应用程序的进程地址,进行读取操作。(当然,这里如果用CreateFileMapping也是可以获取对应的句柄)
HANDLE OpenFileMapping(
DWORD dwDesiredAccess, // access mode
BOOL bInheritHandle, // inherit flag
LPCTSTR lpName // pointer to name of file-mapping object
);
dwDesiredAccess:同MapViewOfFile函数的dwDesiredAccess参数
bInheritHandle :如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE。
lpName :指定要打开的文件映射对象名称。
进行内存映射文件的读写:
一旦MapViewOfFile调用成功,就可以像读写本进程地址空间的内存区一样,进行内存的读写操作了。
//读操作:
if ( m_pViewOfFile )
{
// read text from memory-mapped file
TCHAR s[dwMemoryFileSize];
lstrcpy(s, (LPCTSTR) m_pViewOfFile);
}
//写操作:
if ( m_pViewOfFile )
{
TCHAR s[dwMemoryFileSize];
m_edit_box.GetWindowText(s, dwMemoryFileSize);
lstrcpy( (LPTSTR) m_pViewOfFile, s);
// Notify all running instances that text was changed
::PostMessage(HWND_BROADCAST,
wm_Message,
(WPARAM) m_hWnd,
0);
}
1、Creating Named Shared Memory
To share data, multiple processes can use memory-mappedfiles that the system paging file stores.
FirstProcess
The first process creates the file mapping object bycalling the CreateFileMapping functionwith INVALID_HANDLE_VALUE and a name for the object. By usingthe PAGE_READWRITE flag, the process has read/write permissionto the memory through any file views that are created.
Then the process uses the file mapping object handle that CreateFileMapping returnsin a call to MapViewOfFile tocreate a view of the file in the process address space. The MapViewOfFile functionreturns a pointer to the file view, pBuf. The process then uses the CopyMemory functionto write a string to the view that can be accessed by other processes.
Prefixing the file mapping object names with"Global\" allows processes to communicate with each other even ifthey are in different terminal server sessions. This requires that the firstprocess must have the SeCreateGlobalPrivilege privilege.
When the process no longer needs access to the filemapping object, it should call the CloseHandle function.When all handles are closed, the system can free the section of the paging filethat the object uses.
first process
#include
#include
#include
#include
#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");
TCHAR szMsg[]=TEXT("Message from first process.");
int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
BUF_SIZE, // maximum object size (low-order DWORD)
szName); // name of mapping object
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not create file mapping object (%d).\n"),
GetLastError());
return 1;
}
pBuf = (LPTSTR) MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
return 1;
}
CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
_getch();
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
A second process can access the string written to theshared memory by the first process by calling the OpenFileMapping functionspecifying the same name for the mapping object as the first process. Then itcan use the MapViewOfFile functionto obtain a pointer to the file view, pBuf. The process can display this string as it would anyother string. In this example, the message box displayed contains the message"Message from first process" that was written by the first process.
#include
#include
#include
#include
#pragma comment(lib, "user32.lib")
#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");
int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
szName); // name of mapping object
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not open file mapping object (%d).\n"),
GetLastError());
return 1;
}
pBuf = (LPTSTR) MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (pBuf == NULL)
{
_tprintf(TEXT("Could not map view of file (%d).\n"),
GetLastError());
CloseHandle(hMapFile);
return 1;
}
MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
2、
程序包括一个客户端和一个服务端,服务端创建共享内存,客户端打开共享内存,两者通过两个事件互斥访问共享内存,实现一个小功能,就是服务端进程从控制台读入数据发送给客户端进程。
服务端:
#include "stdafx.h"
#include
#include
using namespace std;
int main()
{
HANDLE hMutex = NULL;
HANDLE hFileMapping = NULL;
LPVOID lpShareMemory = NULL;
HANDLE hServerWriteOver = NULL;
HANDLE hClientReadOver = NULL;
//create share memory
hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
1024*1024,
L"ShareMemoryTest");
if (NULL == hFileMapping)
{
cout << "CreateFileMapping fail:" << GetLastError() << endl;
goto SERVER_SHARE_MEMORY_END;
}
lpShareMemory = MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS,
0,
0, //memory start address
0); //all memory space
if (NULL == lpShareMemory)
{
cout << "MapViewOfFile" << GetLastError() << endl;
goto SERVER_SHARE_MEMORY_END;
}
//373
hMutex = CreateMutex(NULL, FALSE, L"SM_Mutex");
if (NULL == hMutex || ERROR_ALREADY_EXISTS == GetLastError())
{
cout << "CreateMutex" << GetLastError() << endl;
goto SERVER_SHARE_MEMORY_END;
}//多个线程互斥访问
//send data
hServerWriteOver = CreateEvent(NULL,
TRUE,
FALSE,
L"ServerWriteOver");
hClientReadOver = CreateEvent(NULL,
TRUE,
FALSE,
L"ClientReadOver");
if (NULL == hServerWriteOver ||
NULL == hClientReadOver)
{
cout << "CreateEvent" << GetLastError() << endl;
goto SERVER_SHARE_MEMORY_END;
}
char p = 0;
char* q = (char*)lpShareMemory;
do
{
p = getchar();
if (WaitForSingleObject(hClientReadOver, 5*1000) != WAIT_OBJECT_0)
goto SERVER_SHARE_MEMORY_END;
q[0] = p;
if (!ResetEvent(hClientReadOver)) goto SERVER_SHARE_MEMORY_END;//把指定的事件对象设置为无信号状态
if (!SetEvent(hServerWriteOver)) goto SERVER_SHARE_MEMORY_END;//把指定的事件对象设置为有信号状态
} while (p != '\n');
SERVER_SHARE_MEMORY_END:
//release share memory
if (NULL != hServerWriteOver) CloseHandle(hServerWriteOver);
if (NULL != hClientReadOver) CloseHandle(hClientReadOver);
if (NULL != lpShareMemory) UnmapViewOfFile(lpShareMemory);
if (NULL != hFileMapping) CloseHandle(hFileMapping);
if (NULL != hMutex) ReleaseMutex(hMutex);
return 0;
}
客户端
#include "stdafx.h"
#include
#include
using namespace std;
int main()
{
HANDLE hMutex = NULL;
HANDLE hFileMapping = NULL;
LPVOID lpShareMemory = NULL;
HANDLE hServerWriteOver = NULL;
HANDLE hClientReadOver = NULL;
hMutex = OpenMutex(MUTEX_ALL_ACCESS,
FALSE,
L"SM_Mutex");
if (NULL == hMutex)
{
if (ERROR_FILE_NOT_FOUND == GetLastError())
{
cout << "OpenMutex fail: file not found!" << endl;
goto CLIENT_SHARE_MEMORY_END;
}
else
{
cout << "OpenMutex fail:" << GetLastError() << endl;
goto CLIENT_SHARE_MEMORY_END;
}
}
if (WaitForSingleObject(hMutex, 5000) != WAIT_OBJECT_0)//hMutex 一旦互斥对象处于有信号状态,则该函数返回
{
DWORD dwErr = GetLastError();
goto CLIENT_SHARE_MEMORY_END;
}
//open share memory
hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS,
FALSE,
L"ShareMemoryTest");
if (NULL == hFileMapping)
{
cout << "OpenFileMapping" << GetLastError() << endl;
goto CLIENT_SHARE_MEMORY_END;
}
lpShareMemory = MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
if (NULL == lpShareMemory)
{
cout << "MapViewOfFile" << GetLastError() << endl;
goto CLIENT_SHARE_MEMORY_END;
}
//read and write data
hServerWriteOver = CreateEvent(NULL,
TRUE,
FALSE,
L"ServerWriteOver");
hClientReadOver = CreateEvent(NULL,
TRUE,
FALSE,
L"ClientReadOver");
if (NULL == hServerWriteOver ||
NULL == hClientReadOver)
{
cout << "CreateEvent" << GetLastError() << endl;
goto CLIENT_SHARE_MEMORY_END;
}
char p = 0;
char* q = (char*)lpShareMemory;
do
{
if (!SetEvent(hClientReadOver))
goto CLIENT_SHARE_MEMORY_END;
if (WaitForSingleObject(hServerWriteOver, INFINITE) != WAIT_OBJECT_0)
goto CLIENT_SHARE_MEMORY_END;
p = q[0];
putchar(p);
if (!ResetEvent(hServerWriteOver))
goto CLIENT_SHARE_MEMORY_END;
} while (p != '\n');
CLIENT_SHARE_MEMORY_END:
//release share memory
if (NULL != hServerWriteOver) CloseHandle(hServerWriteOver);
if (NULL != hClientReadOver) CloseHandle(hClientReadOver);
if (NULL != lpShareMemory) UnmapViewOfFile(lpShareMemory);
if (NULL != hFileMapping) CloseHandle(hFileMapping);
if (NULL != hMutex) ReleaseMutex(hMutex);
return 0;
}