使用内存映射文件的一般流程:
由于内存映射文件的一致性,即系统保证在同一文件映射对象的多个视图间保持一致。这可以作为进程通讯的基础,而且在Windows上,同一台机器上共享数据的最底层机制都是内存映射文件。
许多应用程序会在运行过程中创建一些数据,并需要将这些数据传输给其他进程,或与其他进程共享这些数据。如果为了共享数据而必须让应用程序在磁盘上创建数据文件并把数据保存在文件中,那将非常不方便。
Microsoft意识到了这一点,并加入了相应的支持,让系统能够创建以页交换文件为后备存储器的内存映射文件,这样就不需要用磁盘上专门的文件来作为后备存储器了。这种方法和为磁盘文件创建内存映射文件的方法几乎完全相同,甚至更简单。一方面,由于不必创建或打开一个专门的磁盘文件,因此不需要调用CreateFile。我们只需要像原来那样调用CreateFileMapping,并将INVALID_HANDLE_VALUE作为hFile参数传入。这告诉系统我们创建的文件映射对象的物理存储器不是磁盘上的文件,而是希望系统从页交换文件中调拨物理存储器。所需分配的存储器大小由CreateFileMapping的dwMaximumSizeHigh和dwMaxinumSizeLow参数决定。
这样,内存映射文件可以分为两种:
一种是普通的文件,它需要一个普通的文件句柄,用于快速的读写文件,这种文件的数据在进程退出后会保存在硬盘上,所以进程在下次运行时可以得到之前的数据;
另一种是页文件,当创建内存映射文件的时候传入无效的句柄,这时会把页文件当作临时的共享存储空间,当进程退出后这些数据是不会存储下来的。因为共享内存通常只关注执行期间的数据共享,所以一般是使用这种内存映射文件。
一旦创建了文件映射对象,并把一个视图映射到了进程的地址空间中,我们就可以像使用任务内存区域一样使用它了。如果想要和其他进程共享数据,那么可以在调用CreateFileMapping的时候将一个以0为终止符的字符串作为pszName参数传入。这样其他想要访问共享数据的进程就能够以同一个名称为参数来调用CreateFileMapping或OpenFileMapping。
当一个进程不再需要访问文件映射对象的时候,应该调用CloseHandle。当所有句柄都已关闭的时候,系统会从页交换文件中收回所有已调拨的存储器。
下面是进程通讯代码及详细注释:
//进程1先启动,用于发送数据,即将数据写入视图#include <windows.h> #include <stdio.h> #include <conio.h> #include <tchar.h> #define BUF_SIZE 256 TCHAR szName[] = TEXT("MyFileMappingObject"); TCHAR szMsg[] = TEXT("Message from first process."); int _tmain() { HANDLE hMapFile; LPCTSTR pBuf; hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BUF_SIZE, szName); if (hMapFile == NULL) { _tprintf(TEXT("Could not create file mapping object (%d).\n"), GetLastError()); return 1; } pBuf = (LPTSTR)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((void*)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR))); //发送数据 _getch(); UnmapViewOfFile(pBuf); CloseHandle(hMapFile); return 0; }
//进程2后启动,用于接收数据,即读取视图的数据#include <windows.h> #include <stdio.h> #include <conio.h> #include <tchar.h> #pragma comment(lib, "user32.lib") #define BUF_SIZE 256 TCHAR szName[] = TEXT("MyFileMappingObject"); int _tmain() { HANDLE hMapFile; LPCTSTR pBuf; hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, szName); if (hMapFile == NULL) { _tprintf(TEXT("Could not open file mapping object (%d). \n"), GetLastError()); return 1; } pBuf = (LPTSTR)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; } MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK); //接收数据并显示UnmapViewOfFile(pBuf); CloseHandle(hMapFile); return 0; }
注意:
在这儿我们又必须提醒一下一个隐含的易错的。
HANDLE hFile = CreateFile( ... );HANDLE hMap = CreateFileMapping( hFile, ... );
如果CreateFile调用失败,函数返回INVALID_HANDLE_VALUE,在调用CreateFileMapping时,系统将创建以页面交换文件为后备存储器的内存映射文件,而不是所希望的以磁盘文件为后备存储器的内存映射文件。而所涉及的内存映射文件创建代码仍然工作正常,所以我们应养成判断函数返回值是否正确的习惯。