与虚拟内存一样,内存映射文件可以用来保留一个地址空间的区域,并将物理存储器提交给该区域。它们之间的差别是,物理存储器来自一个已经位于磁盘上的文件,而不是系统的页文件。一旦该文件被映射,就可以访问它,就像整个文件已经加载内存一样。
内存映射文件可以用于3个不同的目的:
• 系统使用内存映射文件,以便加载和执行. e x e和D L L文件。这可以大大节省页文件空间和应用程序启动运行所需的时间。
• 可以使用内存映射文件来访问磁盘上的数据文件。这使你可以不必对文件执行I / O操作,并且可以不必对文件内容进行缓存。
• 可以使用内存映射文件,使同一台计算机上运行的多个进程能够相互之间共享数据。Wi n d o w s确实提供了其他一些方法,以便在进程之间进行数据通信,但是这些方法都是使用内存映射文件来实现的,这使得内存映射文件成为单个计算机上的多个进程互相进行通信的最有效的方法。
一.内存映射文件的函数包括
CreateFileMapping , OpenFileMapping, MapViewOfFile, UnmapViewOfFile 和 FlushViewOfFile。
用法如下:
1.HANDLE CreateFileMapping(
HANDLE hFile, // 一个文件句柄
LPSECURITY_ATTRIBUTE lpAttributes, // 定义内存映射文件对象是否可以被承
DWORD flProtect, // 该内存映射文件的保护类型
DWORD dwMaximumSizeHigh,// 内存映射文件的长度
DWORD dwMaximumSizeLow, //
LPCTSTR lpName // 内存映射文件的名字
)
hFile 指定要映射的文件的句柄,如果这是一个已经打开的文件的句柄(CreateFile函数的返回值),那么将建立这个文件的内存映射文件,如果这个参数为INVALID_HANDLE_VALUE(-1),则建立共享内存。
lpAttribute 安全属性,一般设为NULL
flProtect 指定映射文件的保护类型,它的取值可以是PAGE_READONLY(内存页面只读) 或PAGE_READWRITE(内存页面可读写)。
dwMaximumSizeHigh 和 dwMaximumSizeLow参数组合指定了一个64位的内存映射文件的长度。一种简单的方法是将这两个参数全部设置为0,那么内存映射文件的大小将与磁盘文件大小一致。
lpName 指定文件映射对象的名字。如存在这个名字的一个映射,函数就会打开它。
用vbNullString可以创建一个无名的文件映射。
2.HANDLE OpenFileMapping(
DWORD dwDesiredAccess, // 指定保护类型
BOOL bIsInheritHandle, // 返回的句柄是否可以被继承
LPCSTR lpName // 创建对象时使用的名字
)
如果创建的是共享内存,其他进程不能再使用CreateFileMapping函数去创建同名的内存映射文件对象,而要使用OpenFileMapping函数打开已创建好的对象。
dwDesiredAcess 指定保护类型有FILE_MAP_ALL_ACCESS、FILE_MAP_WRITE 或FILE_MAP_READ
bInheritHandle Long,如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE。一般为NULL。
lpName String,指定要打开的文件映射对象名称。
3.LPVOID MapViewOfFile(
HANDLE hFileMappingObject, // 前两个函数返回的内存映射文件的句柄
DWORD dwDesiredAcess, // 保护类型FILE_MAP_READ ,FILE_MAP_WRITE,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配
DWORD dwFileOffsetHight, // 从文件的那个地址开始映射
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap // 要映射的字节数,为0则映射整个文件
)
MapViewOfFile是计算机函数,功能是将一个文件映射对象映射到当前应用程序的地址空间。MapViewOfFileEx允许我们指定一个基本地址来进行映射。
如果成功,则返回映射视图文件的开始地址值。
如果失败,则返回 NULL.可调用GetLastError() 查看错误。
4.BOOL UnmapViewOfFile( LPCVOID lpBaseAddress )
当不再使用内存映射文件时,可以通过UmmapViewOfFile函数撤销映射并使用CloseHandle函数关闭内存映射文件的句柄。
lpBaseAddress Long,指定要解除映射的一个文件映射的基准地址。这个地址是早先用MapViewOfFile函数获得的。
返回值Bool,非零表示成功,零表示失败。可以通过GetLastError()获取错误代码。
5.BOOL FlushViewOfFile(
LPCVOID lpBaseAddress, // 开始的地址
SIZE_T dwNumberOfBytesToFlush // 数据块的大小
)
如果修改了映射视图中的内存,系统会在试图撤销映射或文件映射对象被删除时自动将数据写到磁盘上,但程序也可以根据需要将视图中的数据立即写到磁盘上。
lpBaseAddress Any,包含了刷新基本地址的一个Long值(参考注解)。
dwNumberOfBytesToFlush Long,欲刷新的字节数。
返回值编辑 播报
Long,非零表示成功,零表示失败。会设置GetLastError。
注解:
如与远程系统建立了文件映射,那么虽然这个函数可保证数据已在当前系统写入,但不能保证数据实际写入远程系统的磁盘——除非用FILE_FLAG_WRITE_THROUGH 选项打开文件。该选项的作用是禁止写延迟,所有更新的数据都必须立即写入磁盘。
二.使用步骤
1. 使用CreateFileMapping创建一个内存映射文件内核对象,告诉操作系统内存映射文件需要的物理内存大小,这个步骤决定了内存映射文件的用途――究竟是为磁盘上的文件建立内存映射还是为多个进程共享数据建立共享内存。或者使用OpenFileMapping打开映射文件内核对象。
2. 映射文件映射对象的全部或一部分到进程的地址空间,可以认为该操作是为文件中的内容分配线型地址空间,并将线型地址和文件内容对应起来,完成该操作的函数是MapViewOfFile。
三.使用内存映射文件读文件的具体过程可以这样:
(1) 调用CreateFile函数打开想要映射的文件,得到文件句柄hFile。
(2) 调用CreateFileMapping函数,并传入文件句柄hFile,为该文件创建一个内存映射内核对象,得到内存映射文件的句柄hMap。
(3) 调用MapViewOfFile函数映射整个文件或一部分到进程的虚拟地址空间。该函数返回文件映射到内存后的起始地址。使用指向这个地址的指针就可以读取文件的内容了。
(4) 调用UnmapViewOfFile函数来解除文件映射。
(5) 调用CloseHandle函数关闭文件对象,必须传入内存映射文件句柄hMap
(6) 调用CloseHandle函数关闭文件对象,必须传入文件句柄hFile。
四.进程间共享内存:
共享内存主要是通过映射机制实现的。Windows下进程的地址空间是相互隔离的,但在物理上却是重叠的。所谓的重叠是指同一块内存区域可能被多个进程同时使用。当调用CreateFileMapping创建命名的内存映射文件对象时,Windows即在物理内存中申请了一块指定大小的内存区域,返回文件映射对象的新句柄hMap。为了能够访问这块区域必须调MapViewOfiFile函数,促使Windows将此内存空间映射到进程的地址空间中。当在其他进程中访问这块区域时,则必须使用OpenFileMapping函数来取得对象句柄hMap,并调用MapViewOfFile函数得到此内存空间的一个映射。这样一来,系统就把同一块内存区域映射到了不同进程的地址空间中,从而达到共享内存的目的。
示例:
#define BUF_SIZE 256
#define FILE_MAP_NAME "Global//{42561571-5A43-4c5c-9D76-4132D70A8968}"
The first process:
HANDLE hFile = CreateFile("c://mapping.txt",
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if ( !hFile )
{
return;
}
HANDLE hMapFile = NULL;
hMapFile = CreateFileMapping( hFile,
NULL ,
PAGE_READWRITE,
0, /*一定为0 否则磁盘空间不足*/
BUF_SIZE, /*不能为0*/
FILE_MAP_NAME /*Global/" or "Local/" 其他都不能带有/ */
);
//hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE,/*(HANDLE)0xFFFFFFFF*/
// //NULL,
// ShareRestrictedSD.GetSA(),
// PAGE_READWRITE,
// 0,
// BUF_SIZE,
// FILE_MAP_NAME //(L"ShareMemorySZHY" // 共享内存名称参考)
// );
if ( hMapFile == NULL )
{
DWORD dwErr = GetLastError();
return;
}
void* pMapBuf = NULL;
if ( ( pMapBuf = MapViewOfFile(hMapFile,
FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE)) == NULL)
{
DWORD dwErr = GetLastError();
return;
}
memset(pMapBuf, 0, 100);
strcpy( (char *)pMapBuf, "create file text" );
UnmapViewOfFile( pMapBuf );
//CloseHandle( hMapFile );
CloseHandle( hFile );
The Second Process:
HANDLE hMapFile = NULL;
if ( (hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS,
FALSE,
//NULL,
FILE_MAP_NAME
)) == NULL)
{
DWORD dwErr = GetLastError();
return;
}
void* pMapBuf = NULL;
if ( ( pMapBuf = MapViewOfFile(hMapFile,
FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE)) == NULL)
{
DWORD dwErr = GetLastError();
return;
}
strcpy( (char*)pMapBuf, "open file text");
UnmapViewOfFile( pMapBuf );
CloseHandle( hMapFile );
参考链接
c++共享内存(二):通过两个共享内存地址块实现程序A和B的相互通信,并通过CreateSemaphore同步线程,防止程序A和B同时访问同一块内存块