内存映射文件,是由一个文件到一块内存的映射。Win32提供了允许应用程序把文件映射到一个进程的函数 (CreateFileMapping)。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而且在对该文件进行操作之前必须首先对文件进行映射。使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。
// FileMap.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#define BUFFSIZE 1024 //内存大小
#define FILE_MAP_START 0x28804 //文件映射的起始地址
LPTSTR lpcTheFile = TEXT("test.dat");
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hMapFile; // 文件内存映射区域的句柄
HANDLE hFile; // 文件句柄
DWORD dByteWritten; // 写入的字节数
DWORD dwFileSize; // 文件大小
DWORD dwFileMapStart; // 文件映射视图的起始地址
DWORD dwMapViewSize; // 视图大小
DWORD dwFileMapSize; // 文件映射大小
DWORD dwSysGran; // 系统内存分配的粒度
SYSTEM_INFO SysInfo; // 系统信息
LPVOID lpMapAddress; // 内存映射区域的起始地址
PCHAR pData; // 数据
INT i;
INT iData;
INT iViewData;
BYTE cMapBuffer[32]; // 存储从mapping中计出的数据
hFile = CreateFile(lpcTheFile,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile error\n",GetLastError);
return 1;
}
for( i = 0; i < 65535; i++)
{
WriteFile(hFile, &i, sizeof(i),&dByteWritten, NULL);
}
dwFileSize = GetFileSize(hFile,NULL);
printf("文件大小: %d\n", dwFileSize);
//获取系统信息,内存分配粒度
//获取分配粒度,进行下面计算
//目的是为了映射的数据与系统内存分配粒度对齐,提高内存的访问效率
GetSystemInfo(&SysInfo);
dwSysGran = SysInfo.dwAllocationGranularity; // 65536 = 64kb
//计算mapping的起始位置 如果 FILE_MAP_START 为奇数 改成 偶数地址
//分配的粒度是内存最小分配大小。
//比如你在0x10的地方申请分配4097个字节的内存,
//那么实际分配的大小是4097/4k+4K=8K的大小。
dwFileMapStart = (FILE_MAP_START / dwSysGran) * dwSysGran;
//计算mapping_view的大小
dwMapViewSize = (FILE_MAP_START % dwSysGran) + BUFFSIZE;
//计算mapping的大小
dwFileMapSize = FILE_MAP_START + BUFFSIZE;
//计算需要读取的数据的偏移
iViewData= FILE_MAP_START - dwFileMapStart;
//创建File mapping
hMapFile = CreateFileMapping( hFile, //需要映射的文件句柄
NULL, //安全选项:默认
PAGE_READWRITE, //可读,可写
0, //mapping对象的大小,高位
dwFileMapSize, //mapping对象的大小,低位
NULL); //mapping对象的名字
if( hMapFile == NULL)
{
printf("CreateFileMapping error: %d\n",GetLastError());
return 1;
}
lpMapAddress = MapViewOfFile( hMapFile,
FILE_MAP_ALL_ACCESS,
0,
dwFileMapStart,
dwMapViewSize);
if (lpMapAddress == NULL)
{
printf("MapViewOfFile error:%d\n",GetLastError());
return 1;
}
printf("文件map view相当于文件的起始位置: 0x%x\n",dwFileMapStart);
printf("文件map view的大小: x%x\n",dwMapViewSize);
printf("文件mapping对象的大小: x%x\n",dwFileMapSize);
printf("从相对于map view 0x%x 字节的位置读取数据,", iViewData);
//将指向数据的指针偏移,到达我们关心的地方
pData = (PCHAR)lpMapAddress + iViewData;
//读取数据,赋值给变量
iData = *(PINT)pData;
printf("为: x%.8x\n",iData);
//从mapping中复制数据,32个字节,并打印
CopyMemory(cMapBuffer,lpMapAddress,32);
printf("lpMapAddress起始字节是:");
for( i = 0; i < 32; i++)
{
printf("0x%.2x ",cMapBuffer[i]);
}
//将mapping的前32个字节用0xff填充
FillMemory(lpMapAddress,32,(BYTE)0xff);
//将映射的数据写回到硬盘上
FlushViewOfFile(lpMapAddress,dwMapViewSize);
printf("\n已经将lpMapAddress开始的字节使用0xff填充");
//关闭mapping对象
if(!CloseHandle(hMapFile))
{
printf("\nclosing the mapping object error %d!",GetLastError());
}
//关闭文件
if(!CloseHandle(hFile))
{
printf("\nError %ld occurred closing the file!",GetLastError());
}
printf("\n");
return 0;
}
/*
现代计算机读取内存的时候,一般只能在偶数边界上开始读,
什么意思呢,我们打个比方,在32位的机子上,一个int变量
变量占用4字节,假如这个变量的真实物理内存地址是0x400005,
那计算机在取数的时候会先从0x400004取4个字节,
再从0x400008取4个字节,然后这个变量的值就是前4个字节的
后三位和后4个字节的第一位,也就是说如果一个变量的地址从
奇数开始,就可能要多读一次内存,而如果落在从偶数开始,特
别是计算机位数/8的倍数开始,效率就高了
*/