windows内存映射学习及帮助类实现
Posted 小小鸟的大梦想
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了windows内存映射学习及帮助类实现相关的知识,希望对你有一定的参考价值。
本文通过创建文件内存映射类,学习windows内存映射相关知识;创建内存映射文件后,可以按照内存操作方式操作文件
感谢http://blog.csdn.net/csafu/article/details/8602142,
引用部分内容"文件映射问题
内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。
所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识,
内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。
下面给出使用内存映射文件的一般方法:
首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。
在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。
为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉
系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据
作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分
映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,
在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。
这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、
通过CloseHandle()关闭前面创建的文件映射对象和文件对象。"
1 #ifndef MEMFILEMAPHELPER_H 2 #define MEMFILEMAPHELPER_H 3 //文件内存映射类,创建内存映射文件;创建后,可以按照内存操作方式操作文件 4 /* 5 文件映射问题 6 内存映射文件并不是简单的文件I/O操作,实际用到了Windows的核心编程技术--内存管理。 7 所以,如果想对内存映射文件有更深刻的认识,必须对Windows操作系统的内存管理机制有清楚的认识, 8 内存管理的相关知识非常复杂,超出了本文的讨论范畴,在此就不再赘述,感兴趣的读者可以参阅其他相关书籍。 9 下面给出使用内存映射文件的一般方法: 10 11 首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。 12 在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。 13 为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉 14 系统文件的尺寸以及访问文件的方式。在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据 15 作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分 16 映射到进程地址空间。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样, 17 在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。 18 这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、 19 通过CloseHandle()关闭前面创建的文件映射对象和文件对象。 20 */ 21 22 #include <Windows.h> 23 #include <WinBase.h> 24 #include <string> 25 #include <iostream> 26 using namespace std; 27 28 //typedef unsigned char byte; 29 //typedef unsigned long DWORD; 30 //typedef void* HANDLE; 31 32 class CMemFileMapHelper{ 33 public: 34 enum MemFileType{SEQ_READ=0,SEQ_WRITE,RANDOM_READ,RANDOM_WRITE,SEQ_READ_WRITE,RANDOM_READ_WRITE}; 35 protected: 36 HANDLE m_FileHandler;//原始文件句柄 37 HANDLE m_FileMemMapHandler;//内存映射文件句柄 38 unsigned __int64 m_FileSize; 39 byte* m_BaseAddr;//内存映射文件首地址,m_BaseAddr+n =>访问文件第n字节处的位置 40 bool m_FileMapped;//是否映射 41 public: 42 CMemFileMapHelper(){ 43 m_FileMapped = false; 44 m_FileHandler = NULL; 45 m_FileMemMapHandler = NULL; 46 m_BaseAddr = NULL; 47 m_FileSize = 0; 48 } 49 ~CMemFileMapHelper(){ 50 if(m_FileMapped) 51 ReleaseFileMapping(); 52 } 53 54 void ShowError(char* errmsg){ 55 cout<<errmsg<<endl; 56 } 57 58 //将文件加载到内存映射 59 bool BuildFileMapping(const char* fileName,MemFileType type = SEQ_READ, unsigned __int64 view_size=0){ 60 DWORD err_code; 61 char err_msg[100]; 62 string shared_name = GetLastFileName(fileName); 63 64 //存取模式//GENERIC_READ | GENERIC_WRITE 65 DWORD access_mode; 66 67 //共享模式// FILE_SHARE_READ | FILE_SHARE_WRITE 68 DWORD share_mode; 69 70 /*文件属性: 71 FILE_FLAG_WRITE_THROUGH 操作系统不得推迟对文件的写操作 72 FILE_FLAG_OVERLAPPED 允许对文件进行重叠操作 73 FILE_FLAG_NO_BUFFERING 禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块 74 FILE_FLAG_RANDOM_ACCESS 针对随机访问对文件缓冲进行优化 75 FILE_FLAG_SEQUENTIAL_SCAN 针对连续访问对文件缓冲进行优化 76 FILE_FLAG_DELETE_ON_CLOSE 关闭了上一次打开的句柄后,将文件删除。特别适合临时文件 77 */ 78 DWORD mmf_flag; 79 80 /*打开文件方式: 81 CREATE_NEW 创建文件;如文件存在则会出错 82 CREATE_ALWAYS 创建文件,会改写前一个文件 83 OPEN_EXISTING 文件必须已经存在。由设备提出要求 84 OPEN_ALWAYS 如文件不存在则创建它 85 TRUNCATE_EXISTING 讲现有文件缩短为零长度*/ 86 DWORD file_create_mode; 87 88 /*页面内存访问方式: 89 PAGE_EXECUTE 可执行 90 PAGE_EXECUTE_READ 可读,可执行 91 PAGE_EXECUTE_READWRITE 可读,可写,可执行 92 PAGE_EXECUTE_WRITECOPY 可读,可写,可执行,以Read-on-write和copy-on-write方式共享 93 PAGE_NOACCESS 不可访问 94 PAGE_READONLY 只读 并且hFile对应的文件必须以GENERIC_READ形式打开。 95 PAGE_READWRITE 可读,可写 并且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。 96 PAGE_WRITECOPY copy-on-write保护机制 并且hFile对应的文件必须以GENERIC_READ 和 GENERIC_WRITE形式打开。 97 PAGE_GUARD 保护,如果访问则异常(不能单独使用) 98 PAGE_NOCACHE 不进行CPU缓存(不能单独使用) 99 PAGE_WRITECOMBINE write-combined优化(不能单独使用) 100 */ 101 DWORD page_access_mode; 102 103 /*虚拟页面视图访问方式 104 FILE_MAP_WRITE:一个可读写属性的文件视图被创建,保护模式为PAGE_READWRITE 105 FILE_MAP_READ :一个只读属性的文件视图被创建,保护模式为PAGE_READWRITE 或 PAGE_READONLY 106 FILE_MAP_ALL_ACCESS:与FILE_MAP_WRITE模式相同 107 FILE_MAP_COPY:保护模式为PAGE_WRITECOPY时,得到一个视图文件,当你对视图文件写操作时,页面自动交换,并且你所做的修改不会损坏原始数据资料。 108 */ 109 DWORD view_access; 110 111 //文件映射为一个映像,映像的大小=> size_t view_size 112 113 switch(type){ 114 case SEQ_READ: 115 { 116 access_mode = GENERIC_READ; 117 share_mode = FILE_SHARE_READ; 118 mmf_flag = FILE_FLAG_SEQUENTIAL_SCAN; 119 file_create_mode = OPEN_EXISTING; 120 page_access_mode = PAGE_READONLY; 121 view_access = FILE_MAP_READ; 122 view_size = 0;//将整个文件映射为一个映像 123 } 124 break; 125 case RANDOM_READ: 126 { 127 access_mode = GENERIC_READ; 128 share_mode = FILE_SHARE_READ; 129 mmf_flag = FILE_FLAG_RANDOM_ACCESS; 130 file_create_mode = OPEN_EXISTING; 131 page_access_mode = PAGE_READONLY; 132 view_access = FILE_MAP_READ; 133 view_size = 0; 134 } 135 break; 136 case SEQ_WRITE: 137 { 138 access_mode = GENERIC_READ | GENERIC_WRITE; 139 share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; 140 mmf_flag = FILE_FLAG_WRITE_THROUGH;//FILE_FLAG_SEQUENTIAL_SCAN 141 file_create_mode = CREATE_NEW; 142 page_access_mode = PAGE_READWRITE; 143 view_access = FILE_MAP_WRITE; 144 } 145 break; 146 case RANDOM_WRITE: 147 { 148 access_mode = GENERIC_READ | GENERIC_WRITE; 149 share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; 150 mmf_flag = FILE_FLAG_RANDOM_ACCESS; 151 file_create_mode = CREATE_NEW; 152 page_access_mode = PAGE_READWRITE; 153 view_access = FILE_MAP_WRITE; 154 } 155 break; 156 case SEQ_READ_WRITE: 157 { 158 access_mode = GENERIC_READ | GENERIC_WRITE; 159 share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; 160 mmf_flag = FILE_FLAG_SEQUENTIAL_SCAN; 161 file_create_mode = OPEN_ALWAYS; 162 page_access_mode = PAGE_READWRITE; 163 view_access = FILE_MAP_READ|FILE_MAP_WRITE;//FILE_MAP_ALL_ACCESS 164 } 165 break; 166 case RANDOM_READ_WRITE: 167 { 168 access_mode = GENERIC_READ | GENERIC_WRITE; 169 share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; 170 mmf_flag = FILE_FLAG_RANDOM_ACCESS; 171 file_create_mode = OPEN_ALWAYS; 172 page_access_mode = PAGE_READWRITE; 173 view_access = FILE_MAP_READ|FILE_MAP_WRITE;//FILE_MAP_ALL_ACCESS 174 } 175 break; 176 default: 177 return false; 178 } 179 180 //1.创建文件 181 /* 182 HANDLE CreateFile( 183 LPCTSTR lpFileName, //指向文件名的指针 184 DWORD dwDesiredAccess, //访问模式(写/读) 185 DWORD dwShareMode, //共享模式 186 LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针 187 DWORD dwCreationDisposition, //如何创建 188 DWORD dwFlagsAndAttributes, //文件属性 189 HANDLE hTemplateFile //用于复制文件句柄 190 ); 191 返回值 192 如执行成功,则返回文件句柄。 193 INVALID_HANDLE_VALUE表示出错,会设置GetLastError。 194 即使函数成功,但若文件存在,且指定了CREATE_ALWAYS 或 OPEN_ALWAYS,GetLastError也会设为ERROR_ALREADY_EXISTS 195 */ 196 m_FileHandler = CreateFile(fileName,access_mode,share_mode,NULL,file_create_mode,mmf_flag,NULL); 197 err_code = GetLastError(); 198 switch(err_code){ 199 case INVALID_HANDLE_VALUE: 200 sprintf(err_msg,"文件打开失败"); 201 ShowError(err_msg); 202 return false; 203 break; 204 case ERROR_ALREADY_EXISTS: 205 if(m_FileHandler == NULL &&(type == SEQ_WRITE || type == RANDOM_WRITE) ){ 206 sprintf(err_msg,"文件已存在"); 207 ShowError(err_msg); 208 return false; 209 } 210 break; 211 } 212 213 //2.创建文件映射 214 /* 215 HANDLE CreateFileMapping( 216 HANDLE hFile, //物理文件句柄 217 LPSECURITY_ATTRIBUTES lpAttributes, //安全设置, 一般NULL 218 DWORD flProtect, //保护设置 219 DWORD dwMaximumSizeHigh, //高位文件大小 220 DWORD dwMaximumSizeLow, //低位文件大小 221 LPCTSTR lpName //共享内存名称 222 ); 223 224 调用CreateFileMapping的时候GetLastError的对应错误 225 ERROR_FILE_INVALID 如果企图创建一个零长度的文件映射, 应有此报 226 ERROR_INVALID_HANDLE 如果发现你的命名内存空间和现有的内存映射, 互斥量, 信号量, 临界区同名就麻烦了 227 ERROR_ALREADY_EXISTS 表示内存空间命名已经存在 228 */ 229 230 //2.1获取文件大小 231 DWORD fileSizeLow = 0,fileSizeHigh = 0; 232 if(type == SEQ_READ || type == RANDOM_READ || type == SEQ_READ_WRITE || type == RANDOM_READ_WRITE){ 233 fileSizeLow = GetFileSize(m_FileHandler,&fileSizeHigh); 234 //文件长度 235 m_FileSize = ((unsigned __int64)fileSizeHigh << 32) + (unsigned __int64)fileSizeLow; 236 } 237 else 238 { 239 m_FileSize = view_size;//待创建的文件的大小 240 fileSizeHigh = view_size >> 32; 241 fileSizeLow = view_size & 0xFFFFFFFF; 242 } 243 244 //2.2创建映射文件 245 m_FileMemMapHandler = CreateFileMapping(m_FileHandler,NULL,page_access_mode,fileSizeHigh,fileSizeLow,shared_name.c_str()); 246 err_code = GetLastError(); 247 if(m_FileMemMapHandler == NULL){ 248 sprintf(err_msg,"创建映射文件错误"); 249 CloseHandle(m_FileHandler); 250 ShowError(err_msg); 251 return false; 252 } 253 254 switch(err_code){ 255 case ERROR_FILE_INVALID: 256 { 257 sprintf(err_msg,"企图创建一个零长度的文件映射错误"); 258 CloseHandle(m_FileHandler); 259 ShowError(err_msg); 260 return false; 261 } 262 break; 263 case ERROR_INVALID_HANDLE: 264 { 265 sprintf(err_msg,"你的命名内存空间和现有的内存映射, 互斥量, 信号量, 临界区同名"); 266 CloseHandle(m_FileHandler); 267 ShowError(err_msg); 268 return false; 269 } 270 break; 271 case ERROR_ALREADY_EXISTS: 272 { 273 sprintf(err_msg,"内存空间命名已经存在"); 274 CloseHandle(m_FileHandler); 275 ShowError(err_msg); 276 return false; 277 } 278 break; 279 } 280 281 //3.加载映射文件 282 /* 283 LPVOID MapViewOfFile( 284 HANDLE hFileMappingObject, //物理文件句柄 285 DWORD dwDesiredAccess, //对文件数据的访问方式 286 DWORD dwFileOffsetHigh, //文件的偏移地址高位 287 DWORD dwFileOffsetLow, //文件的偏移地址低位 288 DWORD dwNumberOfBytesToMap); 289 290 文件的偏移地址由DWORD型的参数dwFileOffsetHigh和dwFileOffsetLow组成的64位值来指定, 291 而且必须是操作系统的分配粒度的整数倍,对于Windows操作系统,分配粒度固定为64KB 292 293 dwNumberOfBytesToMap:映射文件部分的大小,如果为0,则映射整个文件。 294 返回值: 295 如果成功返回返回映射视图的起始地址,如果失败返回NULL。 296 297 在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明如下: 298 BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); 299 */ 300 301 //3.1动态获取当前操作系统的分配粒度: 302 /*SYSTEM_INFO sinf; 303 GetSystemInfo(&sinf); 304 DWORD dwAllocationGranularity = sinf.dwAllocationGranularity; 305 if(type == SEQ_WRITE || type == RANDOM_WRITE){ 306 int imod = view_size % dwAllocationGranularity; 307 if(imod != 0) 308 view_size = (1+(view_size / dwAllocationGranularity))*dwAllocationGranularity; 309 }*/ 310 311 //3.2把文件数据映射到进程的地址空间 312 m_BaseAddr = (byte*)MapViewOfFile(m_FileMemMapHandler,view_access,0,0,view_size); 313 if(m_BaseAddr != NULL){ 314 m_FileMapped = true; 315 return true; 316 } 317 else{ 318 sprintf(err_msg,"文件数据映射到进程的地址空间错误"); 319 CloseHandle(m_FileMemMapHandler); 320 CloseHandle(m_FileHandler); 321 ShowError(err_msg); 322 return false; 323 } 324 } 325 326 bool ReleaseFileMapping(){ 327 /* 328 在完成对映射到进程地址空间区域的文件处理后,需要通过函数UnmapViewOfFile()完成对文件数据映像的释放,该函数原型声明如下: 329 BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); // lpBaseAddress 映射视图起始地址 330 */ 331 if(!m_FileMapped || m_BaseAddr == NULL) return false; 332 //1.释放文件数据映像 333 UnmapViewOfFile(m_BaseAddr); 334 //2.关闭内存映射句柄 335 CloseHandle(m_FileMemMapHandler); 336 //3.关闭进行内存映射的文件 337 CloseHandle(m_FileHandler); 338 m_FileMapped = false; 339 m_FileHandler = NULL; 340 m_FileMemMapHandler = NULL; 341 m_BaseAddr = NULL; 342 m_FileSize = 0; 343 return true; 344 } 345 string GetLastFileName(const char* pathName){ 346 char spliter = ‘\\‘; 347 int pos = strlen(pathName); 348 for(;pos>=0 &&(*(pathName+pos)) != spliter; pos--); 349 const char* fname = pathName + (pos + 1); 350 string fileName(fname); 351 return fileName; 352 } 353 bool IsFileMapped(){ 354 return m_FileMapped; 355 } 356 unsigned __int64 GetCurFileSize(){ 357 if(m_FileMapped) 358 return m_FileSize; 359 else 360 return -1; 361 } 362 byte* GetMemFileBaseAddr()const{ 363 if(m_FileMapped) 364 return m_BaseAddr; 365 else 366 return NULL; 367 } 368 369 //从相对m_BaseAddr首地址offset位置拷贝len长度的数据到dst;确保dst有足够的内存空间 370 bool GetMemory(void* dst,unsigned __int64 offset, size_t len){ 371 if(offset + len > m_FileSize) return false; 372 memcpy(dst,m_BaseAddr+offset,len); 373 return true; 374 } 375 //向相对m_BaseAddr首地址offset位置写入len长度的src数据;确保src有足够的内存空间 376 bool WriteMemory(void* src,unsigned __int64 offset, size_t len){ 377 /*在使用内存映射文件时,为了提高速度,系统将文件的数据页面进行高速缓存, 378 而且在处理文件映射视图时不立即更新文件的磁盘映像。 379 为解决这个问题可以考虑使用FlushViewOfFile()函数, 380 该函数强制系统将修改过的数据部分或全部重新写入磁盘映像, 381 从而可以确保所有的数据更新能及时保存到磁盘。 382 383 将内存复制到所映射的物理文件上面 384 FlushMapViewOfFile函数可以将内存里面的内容DUMP到物理磁盘上面 385 FlushViewOfFile 把文件映射视图中的修改的内容或全部写回到磁盘文件中 386 BOOL FlushViewOfFile( 387 LPCVOID lpBaseAddress, // 修改内容的起始地址 388 DWORD dwNumberOfBytesToFlush // 修改的字节数目 389 ); 390 函数执行成功返回非零。 391 */ 392 if(offset + len > m_FileSize) return false; 393 memcpy(m_BaseAddr+offset,src,len); 394 FlushViewOfFile(m_BaseAddr+offset,len);//把文件映射视图中的修改的内容或全部写回到磁盘文件中 395 return true; 396 } 397 }; 398 #endif
测试:
1 // Demo.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <iostream> 6 #include <fstream> 7 #include <string> 8 #include "MemFileMapHelper.h" 9 using namespace std; 10 11 12 typedef struct{ 13 double X; 14 double Y; 15 double Z; 16 }stru_pos; 17 18 int main(int argc, char* argv[]) 19 { 20 bool flag; 21 int nSize = 10; 22 char* fileName = "F:\\Code\\cpp\\Demo\\Demo\\test.txt"; 23 24 stru_pos *posArr = new stru_pos[nSize]; 25 for (int i=0;i<nSize;i++) 26 { 27 posArr[i].X = i+1; 28 posArr[i].Y = i+2; 29 posArr[i].Z = i+3; 30 } 31 32 CMemFileMapHelper fh; 33 //seq write 34 flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::SEQ_WRITE,nSize * sizeof(stru_pos)); 35 if(flag){ 36 fh.WriteMemory(posArr,0,nSize*sizeof(stru_pos)); 37 fh.ReleaseFileMapping(); 38 } 39 if(!flag) return -1; 40 41 ////radom write 42 //flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::RANDOM_WRITE,nSize * sizeof(stru_pos)); 43 //if(flag){ 44 // for (int i=nSize-1;i>=0 && flag;i--) 45 // { 46 // flag = fh.WriteMemory(&posArr[i],i*sizeof(stru_pos),sizeof(stru_pos)); 47 // cout<<posArr[i].X <<" "<<posArr[i].Y <<" "<<posArr[i].Z<<endl; 48 // } 49 // fh.ReleaseFileMapping(); 50 //} 51 //if(!flag) return -1; 52 53 //seq read 54 flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::SEQ_READ); 55 for (int i=0;i<nSize && flag;i++) 56 { 57 stru_pos pos; 58 flag = fh.GetMemory(&pos,i*sizeof(stru_pos),sizeof(stru_pos)); 59 cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl; 60 } 61 fh.ReleaseFileMapping(); 62 63 ////random read 64 //flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::RANDOM_READ); 65 //for (int i=nSize - 1;i>=0 && flag;i--) 66 //{ 67 // stru_pos pos; 68 // flag = fh.GetMemory(&pos,i*sizeof(stru_pos),sizeof(stru_pos)); 69 // cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl; 70 //} 71 //fh.ReleaseFileMapping(); 72 73 ////random read write 74 //flag = fh.BuildFileMapping(fileName,CMemFileMapHelper::SEQ_READ_WRITE); 75 //stru_pos pos; 76 //flag = fh.GetMemory(&pos,5*sizeof(stru_pos),sizeof(stru_pos)); 77 //cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl; 78 //pos.X = pos.Y = pos.Z = 13; 79 //flag = fh.WriteMemory(&pos,5*sizeof(stru_pos),sizeof(stru_pos)); 80 //cout<<pos.X <<" "<<pos.Y <<" "<<pos.Z<<endl; 81 //fh.ReleaseFileMapping(); 82 83 delete[] posArr; 84 cin>>flag; 85 return 0; 86 }
以上是关于windows内存映射学习及帮助类实现的主要内容,如果未能解决你的问题,请参考以下文章