Windows提供了多种机制允许进程之间能够共享数据。比如,WM_COPYDATA消息,剪贴板,邮件槽(mailslot),管道(pipe),套接字(socket)等。在同一台机器上共享数据最底层的机制就是内存映射文件。
使用内存映射文件的步骤如下:
1. 创建或打开一个文件内核对象,该对象标识了要用作内存映射文件的那个磁盘文件(CreateFile)
2. 创建一个文件映射内核对象(CreateFileMapping)
3. 告诉系统把文件映射对象的部分或全部映射到进程的地址空间中(MapViewOfFile)
调用CreateFile是为了告诉操作系统文件映射的物理存储器所在的位置。为了告诉系统文件映射对象需要多大的物理存储器,须调用CreateFileMapping。在创建了文件映射对象之后,还需要使用MapViewOfFile来将文件的数据映射到进程的地址空间中。当我们把一个文件映射到地址空间中的时候,不必一下子映射整个文件,可以每次只把一小部分映射到地址空间中。文件中被映射到进程地址空间中的部分被称为视图(View)。
用完内存映射文件之后,须执行以下步骤来做清理工作:
1. 告诉系统取消从进程地址空间对文件映射对象的映射(UnmapViewOfFile)
2. 关闭文件映射内核对象(CloseHandle)
3. 关闭文件内核对象(CloseHandle)
不再需要把文件的数据映射到进程的地址空间时,需要调用UnmapViewOfFile来释放内存区域。如果不这样做,在进程终止之前,区域将得不到释放。
无论以什么方式创建的内核对象,我们都要调用CloseHandle向系统表明我们已经结束使用对象,否则会在进程继续运行的过程中引起资源泄漏。
如果我们希望创建的文件映射的物理存储器不是磁盘上的文件,而是从页交换文件中调拨物理存储器,则只需要调用CreateFileMapping,并将INVALID_HANDLE_VALUE作用hFile参数传入。
当我们创建了文件映射对象,对将其映射到进程地址空间中,我们就可以像使用任何内存区域一样使用它了。如果想要在其他进程共享数据,那么可以在调用CreateFileMapping时指定pszName该文件映射对象的名称。这样,其他进程就可以以该名称来调用CreateFileMapping或者OpenFileMapping,并使用该文件映射对象的数据了。
例子
第一个进程
该进程首先调用CreateFileMapping创建文件映射对象,可以看到传入了INVALID_HANDLE_VALUE参数,这表明该文件映射对象从页交换文件中调拨物理存储器。另外,该文件映射对象命名为“Global\\MyFileMappingObject”。
接着,调用MapViewOfFile创建文件映射对象的视图,并将其返回值赋给pBuf,再调用CopyMemory函数将一个字符串写到该视图中去以让其他进程访问。
当进程不再需要使用文件映射对象,需要调用CloseHandle关闭其句柄。
#include <windows.h>
#include <conio.h>
#include "stdafx.h"
#define BUF_SIZE 256
TCHAR sName[] = _T("Global\\MyFileMappingObject");
TCHAR sNick[] = _T("Message from process 1!");
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL, PAGE_READWRITE, 0, BUF_SIZE, sName);
if (hMapFile == NULL) {
_tprintf(TEXT("Could not create file mapping object (%d).\n"), GetLastError());
return 1;
}
pBuf = (LPCTSTR)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 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, sNick, (_tcslen(sNick) * sizeof(TCHAR)));
_getch();
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
第二个进程
第二个进程首先调用OpenFileMapping函数开打开命名为“Global\\MyFileMappingObject”的文件映射对象。然后调用MapViewOfFile来获得视图的指针,pBuf。获得该指针后,便可以像使用一般的字符串一要来操作该字符串,调用MessgeBox可以显示该来自进程1的字符串。
#include <windows.h>
#include "stdafx.h"
#define BUF_SIZE 256
TCHAR szName[]= _T("Global\\MyFileMappingObject");
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hMapFile;
LPCTSTR pBuf;
hMapFile = OpenFileMapping(FILE_MAP_READ, FALSE, szName);
if (hMapFile == NULL) {
_tprintf(TEXT("Could not open file mapping object (%d).\n"), GetLastError());
return 1;
}
pBuf = (LPTSTR)MapViewOfFile(hMapFile, FILE_MAP_READ, 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, _T("Process2"), MB_OK);
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}