windows内存映射学习及帮助类实现
#ifndef MEMFILEMAPHELPER_H
#define MEMFILEMAPHELPER_H
//文件内存映射类,创建内存映射文件;创建后,可以按照内存操作方式操作文件
/*
文件映射问题
内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。
所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,
内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。
下面给出使用内存映射文件的一般方法: 首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。
在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。
为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉
系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据
作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分
映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,
在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。
这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、
通过CloseHandle()关闭前面创建的文件映射对象和文件对象。
*/ #include <Windows.h>
#include <WinBase.h>
#include <string>
#include <iostream>
using namespace std; //typedef unsigned char byte;
//typedef unsigned long DWORD;
//typedef void* HANDLE; class CMemFileMapHelper{
public:
enum MemFileType{SEQ_READ=,SEQ_WRITE,RANDOM_READ,RANDOM_WRITE,SEQ_READ_WRITE,RANDOM_READ_WRITE};
protected:
HANDLE m_FileHandler;//原始文件句柄
HANDLE m_FileMemMapHandler;//内存映射文件句柄
unsigned __int64 m_FileSize;
unsigned __int64 m_CurOffset;
size_t m_FileChunkSize;//文件分割块大小,当文件太大时,将大文件分割成多个文件块
size_t m_CurChunkSize;//当前文件块大小
DWORD m_MapChunkSize;//当前操作系统的分配粒度,内存映射大小
byte* m_BaseAddr;//内存映射文件首地址,m_BaseAddr+n =>访问当前文件块第n字节处的位置
bool m_FileMapped;//是否映射
DWORD m_ViewAccess;
public:
CMemFileMapHelper(){
m_FileMapped = false;
m_FileHandler = NULL;
m_FileMemMapHandler = NULL;
m_BaseAddr = NULL;
m_FileSize = ;
m_FileChunkSize = ;
m_CurChunkSize = ;
m_MapChunkSize = ;
m_CurOffset = ;
m_ViewAccess=;
}
~CMemFileMapHelper(){
if(m_FileMapped)
ReleaseFileMapping();
} void ShowError(char* errmsg){
cout<<errmsg<<endl;
} //将文件加载到内存映射
bool BuildFileMapping(const char* fileName,MemFileType type = SEQ_READ, unsigned __int64 view_size=){
DWORD err_code;
char err_msg[];
string shared_name = GetLastFileName(fileName); //存取模式//GENERIC_READ | GENERIC_WRITE
DWORD access_mode; //共享模式// FILE_SHARE_READ | FILE_SHARE_WRITE
DWORD share_mode; /*文件属性:
FILE_FLAG_WRITE_THROUGH 操作系统不得推迟对文件的写操作
FILE_FLAG_OVERLAPPED 允许对文件进行重叠操作
FILE_FLAG_NO_BUFFERING 禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块
FILE_FLAG_RANDOM_ACCESS 针对随机访问对文件缓冲进行优化
FILE_FLAG_SEQUENTIAL_SCAN 针对连续访问对文件缓冲进行优化
FILE_FLAG_DELETE_ON_CLOSE 关闭了上一次打开的句柄后,将文件删除。特别适合临时文件
*/
DWORD mmf_flag; /*打开文件方式:
CREATE_NEW 创建文件;如文件存在则会出错
CREATE_ALWAYS 创建文件,会改写前一个文件
OPEN_EXISTING 文件必须已经存在。由设备提出要求
OPEN_ALWAYS 如文件不存在则创建它
TRUNCATE_EXISTING 讲现有文件缩短为零长度*/
DWORD file_create_mode; /*页面内存访问方式:
PAGE_EXECUTE 可执行
PAGE_EXECUTE_READ 可读,可执行
PAGE_EXECUTE_READWRITE 可读,可写,可执行
PAGE_EXECUTE_WRITECOPY 可读,可写,可执行,以Read-on-write和copy-on-write方式共享
PAGE_NOACCESS 不可访问
PAGE_READONLY 只读 并且hFile对应的文件必须以GENERIC_READ形式打开。
PAGE_READWRITE 可读,可写 并且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。
PAGE_WRITECOPY copy-on-write保护机制 并且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。
PAGE_GUARD 保护,如果访问则异常(不能单独使用)
PAGE_NOCACHE 不进行CPU缓存(不能单独使用)
PAGE_WRITECOMBINE write-combined优化(不能单独使用)
*/
DWORD page_access_mode; /*虚拟页面视图访问方式
FILE_MAP_WRITE:一个可读写属性的文件视图被创建,保护模式为PAGE_READWRITE
FILE_MAP_READ :一个只读属性的文件视图被创建,保护模式为PAGE_READWRITE 或 PAGE_READONLY
FILE_MAP_ALL_ACCESS:与FILE_MAP_WRITE模式相同
FILE_MAP_COPY:保护模式为PAGE_WRITECOPY时,得到一个视图文件,当你对视图文件写操作时,页面自动交换,并且你所做的修改不会损坏原始数据资料。
*/ //文件映射为一个映像,映像的大小=> size_t view_size switch(type){
case SEQ_READ:
{
access_mode = GENERIC_READ;
share_mode = FILE_SHARE_READ;
mmf_flag = FILE_FLAG_SEQUENTIAL_SCAN;
file_create_mode = OPEN_EXISTING;
page_access_mode = PAGE_READONLY;
m_ViewAccess = FILE_MAP_READ;
view_size = ;//将整个文件映射为一个映像
}
break;
case RANDOM_READ:
{
access_mode = GENERIC_READ;
share_mode = FILE_SHARE_READ;
mmf_flag = FILE_FLAG_RANDOM_ACCESS;
file_create_mode = OPEN_EXISTING;
page_access_mode = PAGE_READONLY;
m_ViewAccess = FILE_MAP_READ;
view_size = ;
}
break;
case SEQ_WRITE:
{
access_mode = GENERIC_READ | GENERIC_WRITE;
share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
mmf_flag = FILE_FLAG_WRITE_THROUGH;//FILE_FLAG_SEQUENTIAL_SCAN
file_create_mode = CREATE_NEW;
page_access_mode = PAGE_READWRITE;
m_ViewAccess = FILE_MAP_WRITE;
}
break;
case RANDOM_WRITE:
{
access_mode = GENERIC_READ | GENERIC_WRITE;
share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
mmf_flag = FILE_FLAG_RANDOM_ACCESS;
file_create_mode = CREATE_NEW;
page_access_mode = PAGE_READWRITE;
m_ViewAccess = FILE_MAP_WRITE;
}
break;
case SEQ_READ_WRITE:
{
access_mode = GENERIC_READ | GENERIC_WRITE;
share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
mmf_flag = FILE_FLAG_SEQUENTIAL_SCAN;
file_create_mode = OPEN_ALWAYS;
page_access_mode = PAGE_READWRITE;
m_ViewAccess = FILE_MAP_READ|FILE_MAP_WRITE;//FILE_MAP_ALL_ACCESS
}
break;
case RANDOM_READ_WRITE:
{
access_mode = GENERIC_READ | GENERIC_WRITE;
share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
mmf_flag = FILE_FLAG_RANDOM_ACCESS;
file_create_mode = OPEN_ALWAYS;
page_access_mode = PAGE_READWRITE;
m_ViewAccess = FILE_MAP_READ|FILE_MAP_WRITE;//FILE_MAP_ALL_ACCESS
}
break;
default:
return false;
} //1.创建文件
/*
HANDLE CreateFile(
LPCTSTR lpFileName, //指向文件名的指针
DWORD dwDesiredAccess, //访问模式(写/读)
DWORD dwShareMode, //共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针
DWORD dwCreationDisposition, //如何创建
DWORD dwFlagsAndAttributes, //文件属性
HANDLE hTemplateFile //用于复制文件句柄
);
返回值
如执行成功,则返回文件句柄。
INVALID_HANDLE_VALUE表示出错,会设置GetLastError。
即使函数成功,但若文件存在,且指定了CREATE_ALWAYS 或 OPEN_ALWAYS,GetLastError也会设为ERROR_ALREADY_EXISTS
*/
m_FileHandler = CreateFile(fileName,access_mode,share_mode,NULL,file_create_mode,mmf_flag,NULL);
err_code = GetLastError();
switch(err_code){
case INVALID_HANDLE_VALUE:
sprintf(err_msg,"文件打开失败");
ShowError(err_msg);
return false;
break;
case ERROR_ALREADY_EXISTS:
if(m_FileHandler == NULL &&(type == SEQ_WRITE || type == RANDOM_WRITE) ){
sprintf(err_msg,"文件已存在");
ShowError(err_msg);
return false;
}
break;
} //2.创建文件映射
/*
HANDLE CreateFileMapping(
HANDLE hFile, //物理文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全设置, 一般NULL
DWORD flProtect, //保护设置
DWORD dwMaximumSizeHigh, //高位文件大小
DWORD dwMaximumSizeLow, //低位文件大小
LPCTSTR lpName //共享内存名称
); 调用CreateFileMapping的时候GetLastError的对应错误
ERROR_FILE_INVALID 如果企图创建一个零长度的文件映射, 应有此报
ERROR_INVALID_HANDLE 如果发现你的命名内存空间和现有的内存映射, 互斥量, 信号量, 临界区同名就麻烦了
ERROR_ALREADY_EXISTS 表示内存空间命名已经存在
*/ //2.1获取文件大小
DWORD fileSizeLow = ,fileSizeHigh = ;
if(type == SEQ_READ || type == RANDOM_READ || type == SEQ_READ_WRITE || type == RANDOM_READ_WRITE){
fileSizeLow = GetFileSize(m_FileHandler,&fileSizeHigh);
//文件长度
m_FileSize = ((unsigned __int64)fileSizeHigh << ) + (unsigned __int64)fileSizeLow;
}
else
{
m_FileSize = view_size;//待创建的文件的大小
fileSizeHigh = view_size >> ;
fileSizeLow = view_size & 0xFFFFFFFF;
} //2.2创建映射文件
m_FileMemMapHandler = CreateFileMapping(m_FileHandler,NULL,page_access_mode,fileSizeHigh,fileSizeLow,shared_name.c_str());
err_code = GetLastError();//错误类型定义在WinError.h
if(m_FileMemMapHandler == NULL){
sprintf(err_msg,"创建映射文件错误");
CloseHandle(m_FileHandler);
ShowError(err_msg);
return false;
} switch(err_code){
case ERROR_FILE_INVALID:
{
sprintf(err_msg,"企图创建一个零长度的文件映射错误");
CloseHandle(m_FileHandler);
ShowError(err_msg);
return false;
}
break;
case ERROR_INVALID_HANDLE:
{
sprintf(err_msg,"你的命名内存空间和现有的内存映射, 互斥量, 信号量, 临界区同名");
CloseHandle(m_FileHandler);
ShowError(err_msg);
return false;
}
break;
case ERROR_ALREADY_EXISTS:
{
sprintf(err_msg,"内存空间命名已经存在");
CloseHandle(m_FileHandler);
ShowError(err_msg);
return false;
}
break;
} //3.加载映射文件
/*
LPVOID MapViewOfFile(
HANDLE hFileMappingObject, //物理文件句柄
DWORD dwDesiredAccess, //对文件数据的访问方式
DWORD dwFileOffsetHigh, //文件的偏移地址高位
DWORD dwFileOffsetLow, //文件的偏移地址低位
DWORD dwNumberOfBytesToMap); 文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定,
而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB dwNumberOfBytesToMap:映射文件部分的大小,如果为0,则映射整个文件。
返回值:
如果成功返回返回映射视图的起始地址,如果失败返回NULL。 在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明如下:
BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);
*/ //3.1动态获取当前操作系统的分配粒度:
SYSTEM_INFO sinf;
GetSystemInfo(&sinf);
m_MapChunkSize = sinf.dwAllocationGranularity;
m_FileChunkSize = *m_MapChunkSize; //3.2把文件数据映射到进程的地址空间
/*
而在某些特殊行业,经常要面对十几GB乃至几十GB容量的巨型文件,而一个32位进程所拥有的虚拟地址空间只有232 = 4GB,显然不能一次将文件映像全部映射进来。对于这种情况只能依次将大文件的各个部分映射到进程中的一个较小的地址空间。这需要对上面的一般流程进行适当的更改:
1)映射文件开头的映像。
2)对该映像进行访问。
3)取消此映像
4)映射一个从文件中的一个更深的位移开始的新映像。
5)重复步骤2,直到访问完全部的文件数据。
*/
m_CurOffset = ;
if(m_FileSize > m_FileChunkSize)
m_CurChunkSize = m_FileChunkSize;
else
m_CurChunkSize = m_FileSize; m_BaseAddr = (byte*)MapViewOfFile(m_FileMemMapHandler,m_ViewAccess,,,m_CurChunkSize);
if(m_BaseAddr != NULL){
m_FileMapped = true;
return true;
}
else{
err_code = GetLastError();//错误类型定义在WinError.h
switch(err_code)
{
case ERROR_ACCESS_DENIED:
sprintf(err_msg,"文件数据映射到进程的地址空间错误,无权限!");
break;
}
CloseHandle(m_FileMemMapHandler);
CloseHandle(m_FileHandler);
ShowError(err_msg);
return false;
}
} bool ReleaseFileMapping(){
/*
在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明如下:
BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); // lpBaseAddress 映射视图起始地址
*/
if(!m_FileMapped || m_BaseAddr == NULL) return false;
//1.释放文件数据映像
if(m_BaseAddr!=NULL) UnmapViewOfFile(m_BaseAddr);
//2.关闭内存映射句柄
CloseHandle(m_FileMemMapHandler);
//3.关闭进行内存映射的文件
CloseHandle(m_FileHandler);
//重置状态
m_FileMapped = false;
m_FileHandler = NULL;
m_FileMemMapHandler = NULL;
m_BaseAddr = NULL;
m_FileSize = ;
m_FileChunkSize = ;
m_CurChunkSize = ;
m_MapChunkSize = ;
m_CurOffset = ;
m_ViewAccess=;
return true;
} string GetLastFileName(const char* pathName){
char spliter = '\\';
int pos = strlen(pathName);
for(;pos>= &&(*(pathName+pos)) != spliter; pos--);
const char* fname = pathName + (pos + );
string fileName(fname);
return fileName;
}
bool IsFileMapped(){
return m_FileMapped;
}
unsigned __int64 GetCurFileSize(){
if(m_FileMapped)
return m_FileSize;
else
return -;
} bool MapFileChunk(unsigned __int64 offset, size_t len)//前提len <= m_FileChunkSize
{
if(offset+len > m_FileSize) return false;
if(len > m_CurChunkSize) return false;
bool needNewChunk = false;
if((offset < m_CurOffset)||//请求的地址在上一个chunk中
((offset + len) > (m_CurOffset + m_CurChunkSize)))//请求的地址在下一个chunk中
{
m_CurOffset = offset;
DWORD offsetmod = m_CurOffset % m_MapChunkSize;
m_CurOffset -= offsetmod;//文件的偏移地址必须是操作系统的分配粒度的整数倍 if(m_CurOffset + m_FileChunkSize > m_FileSize)
m_CurChunkSize = m_FileSize - m_CurOffset;
else
m_CurChunkSize = m_FileChunkSize; needNewChunk = true;
}
else
needNewChunk = false; if(needNewChunk)
{
//3)取消此映像
//4)映射一个从文件中的一个更深的位移开始的新映像。
UnmapViewOfFile(m_BaseAddr);
m_BaseAddr = (byte*)MapViewOfFile(m_FileMemMapHandler,m_ViewAccess,(m_CurOffset >> ),(m_CurOffset & 0xFFFFFFFF),m_CurChunkSize); if(m_BaseAddr == NULL){
char err_msg[];
DWORD err_code = GetLastError();//错误类型定义在WinError.h
switch(err_code)
{
case ERROR_ACCESS_DENIED:
sprintf(err_msg,"文件数据映射到进程的地址空间错误,无权限!");
break;
}
ShowError(err_msg);
return false;
}
}
return true;
} //从相对m_BaseAddr首地址offset位置拷贝len长度的数据到dst;确保dst有足够的内存空间
bool GetMemory(void* dst,unsigned __int64 offset, size_t len){
if(offset < || offset + len > m_FileSize) return false;
unsigned __int64 curoffset=offset;
size_t curlen = ;
while(len>)
{
if(len > m_FileChunkSize) curlen = m_FileChunkSize;
else curlen = len;
if(!MapFileChunk(curoffset,curlen)) return false;
memcpy((char*)dst+(curoffset - offset),m_BaseAddr+(curoffset - m_CurOffset),curlen);
curoffset += curlen;
len -= curlen;
}
return true;
}
//向相对m_BaseAddr首地址offset位置写入len长度的src数据;确保src有足够的内存空间
bool WriteMemory(void* src,unsigned __int64 offset, size_t len){
/*在使用内存映射文件时,为了提高速度,系统将文件的数据页面进行高速缓存,
而且在处理文件映射视图时不立即更新文件的磁盘映像。
为解决这个问题可以考虑使用FlushViewOfFile()函数,
该函数强制系统将修改过的数据部分或全部重新写入磁盘映像,
从而可以确保所有的数据更新能及时保存到磁盘。 将内存复制到所映射的物理文件上面
FlushMapViewOfFile函数可以将内存里面的内容DUMP到物理磁盘上面
FlushViewOfFile 把文件映射视图中的修改的内容或全部写回到磁盘文件中
BOOL FlushViewOfFile(
LPCVOID lpBaseAddress, // 修改内容的起始地址
DWORD dwNumberOfBytesToFlush // 修改的字节数目
);
函数执行成功返回非零。
*/
if(offset < || offset + len > m_FileSize) return false;
unsigned __int64 curoffset=offset;
size_t curlen = ;
while(len>)
{
if(len > m_FileChunkSize) curlen = m_FileChunkSize;
else curlen = len;
if(!MapFileChunk(curoffset,curlen)) return false;
memcpy(m_BaseAddr+(curoffset - m_CurOffset),(char*)src+(curoffset - offset),curlen);
FlushViewOfFile(m_BaseAddr+(curoffset - m_CurOffset),curlen);//把文件映射视图中的修改的内容或全部写回到磁盘文件中
curoffset += curlen;
len -= curlen;
}
return true;
}
};
#endif
测试:
// Demo.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include "MemFileMapHelper.h"
using namespace std; typedef struct{
double X;
double Y;
double Z;
}stru_pos; int main(int argc, char* argv[])
{
bool flag;
int nSize = ;
char* fileName = "F:\\Code\\cpp\\Demo\\Demo\\test.txt"; stru_pos *posArr = new stru_pos[nSize];
for (int i=;i<nSize;i++)
{
posArr[i].X = i+;
posArr[i].Y = i+;
posArr[i].Z = i+;
} CMemFileMapHelper fh;
//seq write
flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::SEQ_WRITE,nSize * sizeof(stru_pos));
if(flag){
fh.WriteMemory(posArr,,nSize*sizeof(stru_pos));
fh.ReleaseFileMapping();
}
if(!flag) return -; ////radom write
//flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::RANDOM_WRITE,nSize * sizeof(stru_pos));
//if(flag){
// for (int i=nSize-1;i>=0 && flag;i--)
// {
// flag = fh.WriteMemory(&posArr[i],i*sizeof(stru_pos),sizeof(stru_pos));
// cout<<posArr[i].X <<" "<<posArr[i].Y <<" "<<posArr[i].Z<<endl;
// }
// fh.ReleaseFileMapping();
//}
//if(!flag) return -1; //seq read
flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::SEQ_READ);
for (int i=;i<nSize && flag;i++)
{
stru_pos pos;
flag = fh.GetMemory(&pos,i*sizeof(stru_pos),sizeof(stru_pos));
cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl;
}
fh.ReleaseFileMapping(); ////random read
//flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::RANDOM_READ);
//for (int i=nSize - 1;i>=0 && flag;i--)
//{
// stru_pos pos;
// flag = fh.GetMemory(&pos,i*sizeof(stru_pos),sizeof(stru_pos));
// cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl;
//}
//fh.ReleaseFileMapping(); ////random read write
//flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::SEQ_READ_WRITE);
//stru_pos pos;
//flag = fh.GetMemory(&pos,5*sizeof(stru_pos),sizeof(stru_pos));
//cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl;
//pos.X = pos.Y = pos.Z = 13;
//flag = fh.WriteMemory(&pos,5*sizeof(stru_pos),sizeof(stru_pos));
//cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl;
//fh.ReleaseFileMapping(); delete[] posArr;
cin>>flag;
return ;
}
32位程序处理4G以上文件,参考网络内容: “
而在某些特殊行业,经常要面对十几GB乃至几十GB容量的巨型文件,而一个32位进程所拥有的虚拟地址空间只有232 = 4GB,显然不能一次将文件映像全部映射进来。对于这种情况只能依次将大文件的各个部分映射到进程中的一个较小的地址空间。这需要对上面的一般流程进行适当的更改:
1)映射文件开头的映像。
2)对该映像进行访问。
3)取消此映像
4)映射一个从文件中的一个更深的位移开始的新映像。
5)重复步骤2,直到访问完全部的文件数据。
下面给出一段根据此描述而写出的对大于4GB的文件的处理代码:
// 选择文件
CFileDialog fileDlg(TRUE, "*.txt", "*.txt", NULL, "文本文件 (*.txt)|*.txt||", this);
fileDlg.m_ofn.Flags |= OFN_FILEMUSTEXIST;
fileDlg.m_ofn.lpstrTitle = "通过内存映射文件读取数据";
if (fileDlg.DoModal() == IDOK)
{
// 创建文件对象
HANDLE hFile = CreateFile(fileDlg.GetPathName(), GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
TRACE("创建文件对象失败,错误代码:%d\r\n", GetLastError());
return;
}
// 创建文件映射对象
HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hFileMap == NULL)
{
TRACE("创建文件映射对象失败,错误代码:%d\r\n", GetLastError());
return;
}
// 得到系统分配粒度
SYSTEM_INFO SysInfo;
GetSystemInfo(&SysInfo);
DWORD dwGran = SysInfo.dwAllocationGranularity;
// 得到文件尺寸
DWORD dwFileSizeHigh;
__int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
qwFileSize |= (((__int64)dwFileSizeHigh) << 32);
// 关闭文件对象
CloseHandle(hFile);
// 偏移地址
__int64 qwFileOffset = 0;
// 块大小
DWORD dwBlockBytes = 1000 * dwGran;
if (qwFileSize < 1000 * dwGran)
dwBlockBytes = (DWORD)qwFileSize;
while (qwFileSize > 0)
{
// 映射视图
LPBYTE lpbMapAddress = (LPBYTE)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,
(DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF),
dwBlockBytes);
if (lpbMapAddress == NULL)
{
TRACE("映射文件映射失败,错误代码:%d\r\n", GetLastError());
return;
}
// 对映射的视图进行访问
for(DWORD i = 0; i < dwBlockBytes; i++)
BYTE temp = *(lpbMapAddress + i);
// 撤消文件映像
UnmapViewOfFile(lpbMapAddress);
// 修正参数
qwFileOffset += dwBlockBytes;
qwFileSize -= dwBlockBytes;
}
// 关闭文件映射对象句柄
CloseHandle(hFileMap);
AfxMessageBox("成功完成对文件的访问");
}
在本例中,首先通过GetFileSize()得到被处理文件长度(64位)的高32位和低32位值。然后在映射过程中设定每次映射的块大小为1000倍的分配粒度,如果文件长度小于1000倍的分配粒度时则将块大小设置为文件的实际长度。在处理过程中由映射、访问、撤消映射构成了一个循环处理。其中,每处理完一个文件块后都通过关闭文件映射对象来对每个文件块进行整理。CreateFileMapping()、MapViewOfFile()等函数是专门用来进行内存文件映射处理用的。
”
windows内存映射学习及帮助类实现的更多相关文章
- windows内存映射文件
http://shushanyegui.duapp.com/?p=731 在描述内存映射文件之前 我们先来写一个系统通过I/O来读写磁盘文件的小程序 #include "stdafx.h&q ...
- 操作系统学习笔记(三) windows内存管理
//系统物理页面是由 (Page Frame Number Database )简称PFN数据库来进行管理,实际上是一个数组,每个物理页面都对应一个PFN项. 进程的地址空间是通过VAD(Virtua ...
- 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...
- 《windows核心编程系列》十六谈谈内存映射文件
内存映射文件允许开发人员预订一块地址空间并为该区域调拨物理存储器,与虚拟内存不同的是,内存映射文件的物理存储器来自磁盘中的文件,而非系统的页交换文件.将文件映射到内存中后,我们就可以在内存中操作他们了 ...
- C#内存映射文件学习[转]
原文链接 内存映射文件是由一个文件到进程地址空间的映射. C#提供了允许应用程序把文件映射到一个进程的函(MemoryMappedFile.CreateOrOpen).内存映射文件与虚拟内存有些类似, ...
- c++/MFC 封装好的文件内存映射类
整理日: 2015年2月16日 首先介绍内存映射文件操作------函数的用法以及先后执行顺序 // 第一步:创建文件 HANDLE hFile = CreateFileForMapping(_T(& ...
- .NET Framework自带的文件内存映射类
最近一直为文件内存映射发愁,整个两周一直折腾这个东西.在64位系统和32位系统还要针对内存的高低位进行计算.好麻烦..还是没搞定 偶然从MSDN上发现.NET 4.0把内存文件映射加到了.NET类库中 ...
- c++ windows下读取大文件(内存映射)
关于内存映射的基本知识以及一些函数的原型说明,参考博客:http://blog.csdn.net/wcyoot/article/details/7363393 下面是我对于读取一个104M文件大小,使 ...
- 目录_Java内存分配(直接内存、堆内存、Unsafel类、内存映射文件)
1.Java直接内存与堆内存-MarchOn 2.Java内存映射文件-MarchOn 3.Java Unsafe的使用-MarchOn 简单总结: 1.内存映射文件 读文件时候一般要两次复制:从磁盘 ...
随机推荐
- shell 编程中使用到得if语句内判断参数
http://blog.chinaunix.net/uid/21411227/cid-63616-list-1.html 1.判断文件类型 –b 当file存在并且是块文件时返回真 -c 当fil ...
- Visual Studio Code 1.0.1 for python
1. 安 F1健 ext install python E:\test\.vscode下的三个文件 2.launch.json { "version": "0.1.0&q ...
- NDK debug模式
NDK默认是使用NDEBUG宏的,assert也默认不生效,若要开启assert,按以下步骤: 1.編譯NDK代碼時,後面加上NDK_DEBUG=1 ,如: ndk-build NDK_BUILD=1 ...
- 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 8.全局防护盲点的总结下篇
0x01 背景 现在的WEB应用对SQL注入的防护基本都是判断GPC是否开启,然后使用addlashes函数对单引号等特殊字符进行转义.但仅仅使用这样的防护是存在很多盲点的,接上篇http://www ...
- Android使用MVP时应该注意的问题
生命周期:因为Presenter是View创建的,我们需要确保完全地理解View的生命周期,特别是因为它将最有可能去处理状态更新和异步数据.举个例子,每一个Presenter应该在View destr ...
- Java实现深克隆的两种方式
序列化和依次克隆各个可变的引用类型都可以实现深克隆,但是序列化的效率并不理想 下面是两种实现深克隆的实例,并且测试类对两种方法进行了对比: 1.重写clone方法使用父类中的clone()方法实现深克 ...
- [Android]天气App 3 网络数据的请求和Json解析
Android客户端开发,不仅仅是在Android端开发,还需要有相应的后台服务支持,否则的话,客户端的数据就只能放到本地自己做处理.我认为的原生态的App就是对应服务端的Client.他能像浏览 ...
- struts1.3设置编码三种方法
本文所写涉及的struts的版本是1.3x. 一.改写struts的ActionServlet. 重写process()方法: 配置相应的web.xml文件 三.通过Chain来处理: 使用cha ...
- 日期类型的input元素设置默认值为当天
html文件:<input name="" type="date" value="" id="datePicker" ...
- [转]如何制作tizen镜像文件(图文教程)?
http://blog.csdn.net/flydream0/article/details/9163119 之前已讲解了如何下载及编译tizen源码(http://blog.csdn.net/fly ...