内存映射文件
一、 内存映射文件简介
与虚拟内存一样,内存映射文件也可以用来保留一个地址空间的区域,并将物理存储器提交给该区域,与虚拟内存的差别在于,物理才能出奇来自一个已经位于磁盘上的文件,而不是系统的也文件,一旦该文件被映射,就可以访问它,就如同整个文件已经加载内存一样。
内存映射文件可用于3个不同的目的:
第一:系统使用内存映射文件加载.exe和dll文件,从而节省了也文件空间和应用程序启动运行所需的时间。
第二:可以使用内存映射文件来访问磁盘上的数据文件。
第三:可以用来同一台计算机上运行的多个进程之间的数据共享。
二、 基于内存映射的创建进程的过程
当线程调用CreateProcess是,系统将执行下列操作步骤
1、系统找出在调用CreateProcess是设定的.exe文件,若找不到则创建失败,返回FALSE。
2、系统创建一个新进程内核对象。
3、系统为这新进程创建一个私有地址空间。
4、系统保存一个足够大的地址空间,用于存放.exe文件,该区域需要的位置在.exe文件本身中设定,按照默认设置,.exe文件的基地址是0x00400000。
5、当.exe文件被映射奥进程的地址空间之后,系统将访问.exe文件的IAT部分,该部分列出了包含.exe文件中的代码要调用的函数的dll文件,然后系统为每个dll文件调用LoadLibrary函数,若任何一个dll需要更多的dll,那些系统将调用Loadlibrary函数,以便加载这些dll。
当调用LoadLibrary函数时,系统将执行系列操作步骤:
(1)、系统保留一个足够大的地址空间区域,用于存放该dll,该区域所需要的位置同样在dll文件本身中设定(具体内存参考PE文件结构)。Windows提供的所有标准系统DLL都拥有不同的基地址。
(2)、如果系统无法在该DLL的首选基地址上保留一个区域,(已经被另一个DLL或EXE占用,或者该区域不够大),此时,系统将寻找另一个地址空间来保留该DLL,并在该DLL中执行重定位操作。
三、使用内存映射文件
1、创建或打开一个文件内核对象
HANDLE CreateFile(
PCSTR pszFileName, /*需要创建或打开的文件名称*/
DWORD dwDesiredAccess, /*读写保护属性*/
DWORD dwShareMode, /*如何共享*/
PSECURITY_ATTRIBUTES psa, /*安全性描述符,可设置为NULL*/
DWORD dwCreationDisposition, /*创建方式标识*/
DWORD dwFlagsAndAttributes, /*创建属性标识*/
HANDLE hTemplateFile);
DWORD dwDesiredAccess可取值
GENERIC_READ:允许对设备进行读访问
GENERIC_WRITE:允许对设备进行写访问(可组合使用)
0:只允许获取与一个设备有关的信息
DWORD dwShareMode可取值
0:不共享
FILE_SHARE_READ:读共享
FILE_SHARE_WRITE:写共享
DWORD dwCreationDisposition可取值
CREATE_NEW 创建文件,如文件存在则会出错
CREATE_ALWAYS 创建文件,会改写前一个文件
OPEN_EXISTING 文件必须已经存在。由设备提出要求
OPEN_ALWAYS 如文件不存在则创建它
TRUNCATE_EXISTING 将现有文件缩短为零长度
DWORD dwFlagsAndAttributes可取值
FILE_ATTRIBUTE_ARCHIVE 标记归档属性
FILE_ATTRIBUTE_COMPRESSED 将文件标记为已压缩,或标记为文件在目录中的默认压缩方式
FILE_ATTRIBUTE_NORMAL 默认属性
FILE_ATTRIBUTE_HIDDEN 隐藏文件或目录
FILE_ATTRIBUTE_READONLY 文件为只读
FILE_ATTRIBUTE_SYSTEM 文件为系统文件
FILE_FLAG_WRITE_THROUGH 操作系统不得推迟对文件的写操作
FILE_FLAG_OVERLAPPED 允许对文件进行重叠操作
FILE_FLAG_NO_BUFFERING 禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块
FILE_FLAG_RANDOM_ACCESS 针对随机访问对文件缓冲进行优化
FILE_FLAG_SEQUENTIAL_SCAN 针对连续访问对文件缓冲进行优化
FILE_FLAG_DELETE_ON_CLOSE 关闭了上一次打开的句柄后,将文件删除。特别适合临时文件
2、创建一个文件映射内核对象
HANDLE CreateFileMapping(
HANDLE hFile, /*想要映射到进程地址空间中的文件句柄*/
PSECURITY_ATTRIBUTES pas, /*安全性描述符*/
DWORD fdwProtect, /*读写保护属性*/
DWORD dwMaximumSizeHigh, /*该文件最大字节数的高位*/
DWORD dwMaximuxSizeLow, /*该文件最大字节数的地位*/
PCTSTR pszName); /*内核对象名称,用于其他进程共享该文件映射对象*/
DWORD fdwProtect的可取值
PAGE_READONLY:以只读方式打开映射
PAGE_READWRITE:以可读、可写方式打开映射
PAGE_WRITECOPY:为写操作留下备份可组合使用下述一个或多个常数
SEC_COMMIT:为文件映射一个小节中的所有页分配内存
SEC_IMAGE:文件是个可执行文件
SEC_RESERVE:为没有分配实际内存的一个小节保留虚拟内存空间
3、将文件数据映射到进程的地址空间
PVOID MapViewOfFile(
HANDLE hFileMappingObject, /*文件映射内核对象的句柄*/
DWORD dwDesiredAccess, /*如何访问该数据*/
DWORD dwFileOffsetHigh, /*需要映射的第一个字节的位置的高位*/
DWORD dwFileOffsetLov, /*需要映射的第一个字节的位置的低位*/
SIZE_T dwNumberOfBytesToMap); /*需要映射到地址空间的字节数*/
DWORD dwDesiredAccess可取值:
FILE_MAP_WRITE:映射可读可写。文件映射对象必须通过PAGE_READWRITE访问创建
FILE_MAP_READ:映射只读。文件映射对象必须通过PAGE_READ或PAGE_READWRITE创建
FILE_MAP_ALL_ACCESS 与FILE_MAP_WRITE相同
FILE_MAP_COPY 映射时保留写操作的副本。文件映射对象必须用PAGE_WRITECOPY创建
4、更新磁盘
BOOL FlushViewOfFile(
PVOID pvAddress, /*需要更新的数据在映射文件中的地址*/
SIZE_T dwNumberOfBytesToFlush); /*需要刷新的字节数*/
5、从进程的地址空间中撤销文件数据的映射
BOOL UnmapViewOfFile(
PVOID pvBaseAddress); /*返回区域的基地址,需保持与MapViewOfFile返回值一致*/
6、关闭文件映射对象和文件对象
CloseHandle(hFileMapping); /*关闭文件映射对象*/
CloseHandle(hFile); /*关闭文件对象*/
使用内存映射文件:
#include <iostream>
#include <windows.h>
using namespace std;
void main()
{
/*创建硬盘文件,若不存在创建之,若存在打开之*/
HANDLE file = CreateFile("hello.txt", GENERIC_WRITE|GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if(file == INVALID_HANDLE_VALUE)
{
cout << "create file false !" << endl;
}
/*创建文件映射内核对象*/
HANDLE fileMap = CreateFileMapping(file, NULL, PAGE_READWRITE, 0, 512, NULL);
if(fileMap == NULL)
{
cout << "create file mapping false !" << endl;
}
/*将文件数据映射到进程的地址空间*/
void * viewFile = MapViewOfFile(fileMap, FILE_MAP_ALL_ACCESS, 0, 0, 512);
if(viewFile == NULL)
{
cout << "view view of file is filse!" << endl;
}
char * str = "hello world !";
memcpy(viewFile, str, strlen(str) + 1);
FlushViewOfFile(viewFile, sizeof(int));
CloseHandle(fileMap);
CloseHandle(file);
system("pause");
}
使用内存映射文件实现进程间数据共享
#include <iostream>
#include <windows.h>
using namespace std;
void main()
{
/*创建由页文件支持的命名文件映射内核对象*/
HANDLE fileMap1 = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, 512, "SharedFile1");
if(fileMap1 == NULL)
{
cout << "create file mapping false !" << endl;
}
/*将文件数据映射到进程的地址空间*/
void * viewFile1 = MapViewOfFile(fileMap1, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if(viewFile1 == NULL)
{
cout << "view view of file is filse!" << endl;
}
/*创建由页文件支持的命名文件映射内核对象*/
HANDLE fileMap2 = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, 512, "SharedFile2");
if(fileMap1 == NULL)
{
cout << "create file mapping false !" << endl;
}
/*将文件数据映射到进程的地址空间*/
void * viewFile2 = MapViewOfFile(fileMap2, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if(viewFile2 == NULL)
{
cout << "view view of file is filse!" << endl;
}
char str1[512];
char str2[512];
strcpy(str1, "init string !");
strcpy(str2, "init string !");
memcpy(viewFile1, str1, strlen(str1) + 1);
memcpy(viewFile2, str2, strlen(str1) + 1);
while(true)
{
cin >> str1;
if(!strcmp(str1, "q"))
{
break;
}
memcpy(viewFile1, str1, strlen(str1) + 1);
cout << (char *)viewFile2 << endl;
}
system("pause");
}