注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

内存映射文件的使用  

2010-02-02 11:00:57|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

内存映射文件的使用

今天写内存映射的共享机制,仔细的在网络上和MSDN上非常认真的读着资料,发现了如下几个问题,首先看一下我的代码:
CString strError;
 CString pathName = _T("d:\\111.txt");
 CString lastName = _T("d:\\111_end.txt");
 HANDLE hFile = CreateFile(pathName,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ, NULL,
  OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);

 if (hFile == INVALID_HANDLE_VALUE)
 {
  strError.Format(_T("创建文件对象失败,错误代码:%d"),GetLastError());
  AfxMessageBox(strError);
  return;
 }
 // 创建文件映射对象---下面的太大,是为测试PC内存映射的,可自己减少到某种程序,比如64K
 HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0x400000, NULL);
 if (hFileMap == NULL)
 {
  DWORD it = GetLastError() ;//ERROR_ALREADY_EXISTS
  strError.Format(_T("创建文件映射对象失败,错误代码:%d"),GetLastError());
  TRACE(_T("创建文件映射对象失败,错误代码:%d\r\n"), it);
  AfxMessageBox(strError);
  return;
 }

 // 得到系统分配粒度---内存映射文件的偏移地址:dwMaximumSizeHigh,dwMaximumSizeLow必须是
 // 文件粒度大小的位数,WINDOWS目前为止统一为64KB。                               
 SYSTEM_INFO SysInfo;
 GetSystemInfo(&SysInfo);
 DWORD dwGran = SysInfo.dwAllocationGranularity;
 // 得到文件尺寸
 DWORD dwFileSizeHigh;
 __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
 qwFileSize |= (((__int64)dwFileSizeHigh) << 32);///MSDN

 // 偏移地址
 __int64 qwFileOffset = 0;
 __int64 T_newmap = 900 * dwGran;
 // 块大小
 DWORD dwBlockBytes = 1000 * dwGran;//文件数据分段大小
 if (qwFileSize - qwFileOffset < dwBlockBytes)
  dwBlockBytes = (DWORD)qwFileSize;

 // 映射视图
 //char *lpbMapAddress = (char *)MapViewOfFile(hFileMap,FILE_MAP_READ,
 // (DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF),dwBlockBytes);

 char *lpbMapAddress = (char *)MapViewOfFile(hFileMap,FILE_MAP_READ | FILE_MAP_WRITE,0,

0,0);
 if (lpbMapAddress == NULL)
 {
  TRACE(_T("映射文件映射失败,错误代码:%d "), GetLastError());
  return;
 }

 //文件逻辑大小  
 DWORD   dwFileSize   =   GetFileSize(hFile,   NULL);
 //文件实际大小  
 DWORD   dwCompressSize   =   GetCompressedFileSize(pathName,   NULL);

 char *temp = lpbMapAddress;
 lpbMapAddress += 30;
 for (int i = 0;i < 300;i++)
 {
  *lpbMapAddress++ = '1';
 }

 //强制写入
 FlushViewOfFile((LPCVOID)lpbMapAddress, 1024);

  //重定义大小
  HANDLE hFileEnd = CreateFile(lastName,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ, NULL,
  OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL);

 if (hFileEnd == INVALID_HANDLE_VALUE)
 {
  strError.Format(_T("创建文件对象失败,错误代码:%d"),GetLastError());
  AfxMessageBox(strError);
  return;
 }
 // 创建文件映射对象
 HANDLE hFileMapEnd = CreateFileMapping(hFileEnd, NULL, PAGE_READWRITE, 0, 330, NULL);
 if (hFileMapEnd == NULL)
 {
  DWORD it = GetLastError() ;//ERROR_ALREADY_EXISTS
  strError.Format(_T("创建文件映射对象失败,错误代码:%d"),GetLastError());
  TRACE(_T("创建文件映射对象失败,错误代码:%d\r\n"), it);
  AfxMessageBox(strError);
  return;
 }

 char *lpbMapAddressEnd = (char *)MapViewOfFile(hFileMapEnd,FILE_MAP_READ |

FILE_MAP_WRITE,0, 0,330);

 //拷贝数据
 memmove(lpbMapAddressEnd,temp,330);

  //释放最后数据块映射
 UnmapViewOfFile(lpbMapAddressEnd);
 UnmapViewOfFile(lpbMapAddress);
 // 关闭文件映射对象句柄
 CloseHandle(hFileMap);
 CloseHandle(hFile);
 CloseHandle(hFileMapEnd);
 CloseHandle(hFileEnd);

因为是在测试所以代码写得比较乱,整体的原理就是先写到一个临时的内存映射文件然后保持刷新,然后在最

后重新建立一个真正的内存映射文件保存实际的文件。

在程序一开始建立临时内存映射时很顺利,我的程序完成了既定的功能,但到拷贝到真正的文件内存映射时,

发现始终无法保存,后来才发现是把临时映射的基指针进行了递增,原来处理图像时就犯过这类错误,又来了

,太马虎。修改后成功,但比照资料特别是MSDN,发现我的 
        //强制写入
 FlushViewOfFile((LPCVOID)lpbMapAddress, 1024);
这段代码应该是不正确的,可是为什么仍然能正常的写入物理文件呢?MSDN上讲:
lpBaseAddress
A pointer to the base address of the byte range to be flushed to the disk representation of the

mapped file.

也就是说刷新的应该是这个基地址到指定大小的位置上的脏数据,我记得在别的文件处理时,即使不做FLUSH

相似的动作,如果直接关闭文件,应该也可以正确的保存数据,所以怀疑是不是UnmapViewOfFile或者

CloseHandle起到了作用。注释掉这句,果然如此。
换句话说,在实时保存数据时,要注意脏数据的写回范围,即指针的起始值。小数据量就算了,如果真得是几G的数据,估计就麻烦了。一般来讲,正常的操作文件的方法无论是LINUX,UNIX,WINDOWS基本都是一个过程,只是可能用得函数不同或者多一个少一个步骤亦或多一种少一种功能。
也就是说,在UnmapViewOfFile、CloseHandle和系统回收物理内存的时候写入磁盘。当进程结束时,系统会自

动关闭该进程打开的所有Handle,然后磁盘回写。当然如果是内核代码异常,导致系统崩溃,就要另当别论了

。而FlushViewOfFile是为了实现程序实时控制回写磁盘而提供的,比如在一个大日志的操作上,如果使用此

种方法就可以保证日志的最大的安全。

做一回活儿,长一些见识,更加认识到自己的不足,所以要更加的努力!!!!!!

  评论这张
 
阅读(1012)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017