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; }