Windows上的文件映射与内存共享
一、概念
文件映射:将磁盘上的文件映射入内存,所谓映射就是先在内存中分配一块内存预备使用,在使用的时候再将文件加载入内存。
内存共享:在进程A中开辟一块内存,然后将这块内存映射到进程B中(进程B实际也会开辟一块内存空间),进程A与B访问它们物理上各自开辟的空间,但是逻辑上是访问同一块内存。
二、文件映射
先使用CreateFile或OpenFile打开文件,获得文件句柄,使用CreateFileMapping创建一个文件映射对象并获得该对象的句柄,使用文件映射对象的句柄调用MapViewOfFileEx创建文件映射视图并开辟一块内存空间供文件映射使用(也可以指定分配哪块内存,最后一个参数指定地址即可,也可以为NULL,这样是操作系统选定分配的地址)。
PS:指定的lpBaseAddress必须是系统内存分配粒度的倍数,否则函数会失败。
使用到的函数,如下
HANDLE CreateFileMappingA( [in] HANDLE hFile,//打开的文件句柄 [in, optional] LPSECURITY_ATTRIBUTES lpFileMappingAttributes,//NULL [in] DWORD flProtect,//权限 [in] DWORD dwMaximumSizeHigh,//0 [in] DWORD dwMaximumSizeLow,//buffer大小 [in, optional] LPCSTR lpName //映射对象的名字 ); LPVOID MapViewOfFileEx( [in] HANDLE hFileMappingObject, [in] DWORD dwDesiredAccess,//权限 [in] DWORD dwFileOffsetHigh, //0 [in] DWORD dwFileOffsetLow, //0 [in] SIZE_T dwNumberOfBytesToMap,//buffer大小 [in, optional] LPVOID lpBaseAddress //指定在地址处分配空间 ); BOOL UnmapViewOfFile( [in] LPCVOID lpBaseAddress );
示例:
int _tmain(void) { HANDLE hMapFile; LPCTSTR pBuf; DWORD size; hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, // use paging file NULL, // default security PAGE_READWRITE | SEC_COMMIT | SEC_LARGE_PAGES, 0, // max. object size size, // buffer size szName); // name of mapping object pBuf = (LPTSTR) MapViewOfFile(hMapFile, // handle to map object FILE_MAP_ALL_ACCESS | FILE_MAP_LARGE_PAGES, // read/write permission 0, 0, BUF_SIZE); UnmapViewOfFile(pBuf); CloseHandle(hMapFile); }
我们使用MapViewOfFileEx返回的地址进行读写映射进来的文件,在修改完毕后操作系统不会马上写回磁盘上的文件,调用UnmapViewOfFile后会写回,但是同时会销毁视图,所以我们可以使用FlushViewOfFile来进行及时的修改返回。
BOOL FlushViewOfFile( [in] LPCVOID lpBaseAddress, [in] SIZE_T dwNumberOfBytesToFlush );
PS:
通过映射视图修改文件时,最后修改时间戳可能不会自动更新。
文件的映射视图不能保证与 ReadFile或 WriteFile函数正在访问的文件一致。
MapViewOfFileEx可以处理远程文件,但它并不能保持它们的连贯性。
三、内存共享
当CreateFileMapping中的文件句柄为-1时,则创建的映射对象用于共享内存。
一个进程可以使用 DuplicateHandle函数将文件映射对象句柄复制到另一个进程中,或者另一个进程可以使用OpenFileMapping函数按名称打开文件映射对象 。
另一端的进程同样也需使用MapViewOfFileEx构建视图,其他的步骤与文件映射相同。
HANDLE OpenFileMappingW( [in] DWORD dwDesiredAccess,//权限 [in] BOOL bInheritHandle, //false [in] LPCWSTR lpName //对象名字,与Create的相同 );
要获取视图的大小,请使用 VirtualQueryEx函数。
SIZE_T VirtualQueryEx( [in] HANDLE hProcess, [in, optional] LPCVOID lpAddress, [out] PMEMORY_BASIC_INFORMATION lpBuffer, [in] SIZE_T dwLength );
lpAddress指向要查询的页面区域的基地址的指针。
lpBuffer指向 MEMORY_BASIC_INFORMATION结构的指针,在该结构中返回有关指定页面范围的信息。