依次将大文件的各个部分映射到进程中的一个较小的地址空间

2024-01-21 21:38

本文主要是介绍依次将大文件的各个部分映射到进程中的一个较小的地址空间,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 文件操作是应用程序最为基本的功能之一,Win32 API和MFC均提供有支持文件处理的函数和类。一般来说,这些函数可以满足大多数场合的要求,但是对于某些特殊应用领域所需要的动辄几十GB、几百GB、乃至几TB的海量存储,再以通常的文件处理方法进行处理显然是行不通的。目前,对于上述这种大文件的操作一般是以内存映射文件的方式来加以处理的。
内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。另外,实际工程中的系统往往需要在多个进程之间共享数据,如果数据量小,处理方法是灵活多变的,如果共享数据容量巨大,那么就需要借助于内存映射文件来进行。实际上,内存映射文件正是解决本地多个进程间数据共享的最有效方法。


    首先要通过CreateFile()函数来创建或打开一个文件内核对象,这个对象标识了磁盘上将要用作内存映射文件的文件。在用CreateFile()将文件映像在物理存储器的位置通告给操作系统后,只指定了映像文件的路径,映像的长度还没有指定。为了指定文件映射对象需要多大的物理存储空间还需要通过CreateFileMapping()函数来创建一个文件映射内核对象以告诉系统文件的尺寸以及访问文件的方式。
CreateFileMapping()在创建了文件映射对象后,还必须为文件数据保留一个地址空间区域,并把文件数据作为映射到该区域的物理存储器进行提交。由MapViewOfFile()函数负责通过系统的管理而将文件映射对象的全部或部分映射到进程地址空间,实际上相当于加载文件中指定的数据到内存中。此时,对内存映射文件的使用和处理同通常加载到内存中的文件数据的处理方式基本一样,在完成了对内存映射文件的使用时,还要通过一系列的操作完成对其的清除和使用过资源的释放。这部分相对比较简单,可以通过UnmapViewOfFile()完成从进程的地址空间撤消文件数据的映像、通过CloseHandle()关闭前面创建的文件映射对象和文件对象。
实际上操作文件映射对象就相当于操作VC++文件读写方式下的文件内部指针。
而在某些特殊行业,经常要面对十几GB乃至几十GB容量的巨型文件,而一个32位进程所拥有的虚拟地址空间只有232 = 4GB,显然不能一次将文件映像全部映射进来。对于这种情况只能依次将大文件的各个部分映射到进程中的一个较小的地址空间。这需要对上面的一般流程进行适当的更改:
1)映射从文件开头的映像;
2)对该映像进行访问;
3)取消此映像;
4)映射一个从文件中的一个更深的位移开始的新映像;
5)重复步骤2,直到访问完全部的文件数据。


示例代码:
    在本例中,首先通过GetFileSize()得到被处理文件长度(64位)的高32位和低32位值。然后在映射过程中设定每次映射的块大小为1000倍的分配粒度(系统的数据分块大小),如果文件长度小于1000倍的分配粒度时则将块大小设置为文件的实际长度。在处理过程中由映射、访问、撤消映射构成了一个循环处理。其中,每处理完一个文件块后都通过关闭文件映射对象来对每个文件块进行整理。CreateFileMapping()、MapViewOfFile()等函数是专门用来进行内存文件映射处理用的。

       // 创建文件对象
 HANDLE hFile = ::CreateFile(strFile,GENERIC_READ,FILE_SHARE_READ, NULL, 
  OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS,NULL);
 if (hFile == INVALID_HANDLE_VALUE)
 {
  TRACE("创建文件对象失败,错误代码:%d\r\n", GetLastError());
  return;
 }
 // 创建文件映射对象
 HANDLE hFileMap = CreateFileMapping(hFile, NULL,PAGE_READONLY, 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);///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);
 if (lpbMapAddress == NULL)
 {
  TRACE("映射文件映射失败,错误代码:%d ", GetLastError());
  return;
 } 
 // 关闭文件对象
 CloseHandle(hFile); 
 ///读文件数据
 while(qwFileOffset < qwFileSize)
 {
  /********************           读文件            ***************************/  
  //read_eh(&lpbMapAddress)读取已映射到内存的数据,

  //并将文件指针作相应后移(lpbMapAddress++),返回指针偏移量
  qwFileOffset = qwFileOffset +read_eh(&lpbMapAddress); //修改偏移量
  if (qwFileOffset > T_newmap)
  {//当数据读到90%时,为防数据溢出,需要映射在其后的数据  T_newmap
   UnmapViewOfFile(lpbMapAddress);//释放当前映射
   if ((DWORD)(qwFileSize - T_newmap)< dwBlockBytes)
   dwBlockBytes = (DWORD)(qwFileSize -T_newmap);
   lpbMapAddress = (char*)MapViewOfFile(hFileMap,FILE_MAP_READ,
   (DWORD)(T_newmap >> 32),(DWORD)(T_newmap & 0xFFFFFFFF),dwBlockBytes);
   // 修正参数
   lpbMapAddress = lpbMapAddress +qwFileOffset - T_newmap;
   T_newmap =T_newmap  + 900 *dwGran;
   if (lpbMapAddress == NULL)
   {
    TRACE("映射文件映射失败,错误代码:%d ",GetLastError());
    return;
   } 
  }
 }
 //释放最后数据块映射
 UnmapViewOfFile(lpbMapAddress);
 // 关闭文件映射对象句柄
 CloseHandle(hFileMap); 

 

这篇关于依次将大文件的各个部分映射到进程中的一个较小的地址空间的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/630897

相关文章

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

使用MapStruct实现Java对象映射的示例代码

《使用MapStruct实现Java对象映射的示例代码》本文主要介绍了使用MapStruct实现Java对象映射的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、什么是 MapStruct?二、实战演练:三步集成 MapStruct第一步:添加 Mave

MySQL8.0临时表空间的使用及解读

《MySQL8.0临时表空间的使用及解读》MySQL8.0+引入会话级(temp_N.ibt)和全局(ibtmp1)InnoDB临时表空间,用于存储临时数据及事务日志,自动创建与回收,重启释放,管理高... 目录一、核心概念:为什么需要“临时表空间”?二、InnoDB 临时表空间的两种类型1. 会话级临时表

C#使用SendMessage实现进程间通信的示例代码

《C#使用SendMessage实现进程间通信的示例代码》在软件开发中,进程间通信(IPC)是关键技术之一,C#通过调用WindowsAPI的SendMessage函数实现这一功能,本文将通过实例介绍... 目录第一章:SendMessage的底层原理揭秘第二章:构建跨进程通信桥梁2.1 定义通信协议2.2

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

SpringBoot路径映射配置的实现步骤

《SpringBoot路径映射配置的实现步骤》本文介绍了如何在SpringBoot项目中配置路径映射,使得除static目录外的资源可被访问,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一... 目录SpringBoot路径映射补:springboot 配置虚拟路径映射 @RequestMapp

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Linux查询服务器 IP 地址的命令详解

《Linux查询服务器IP地址的命令详解》在服务器管理和网络运维中,快速准确地获取服务器的IP地址是一项基本但至关重要的技能,下面我们来看看Linux中查询服务器IP的相关命令使用吧... 目录一、hostname 命令:简单高效的 IP 查询工具命令详解实际应用技巧注意事项二、ip 命令:新一代网络配置全

一文解密Python进行监控进程的黑科技

《一文解密Python进行监控进程的黑科技》在计算机系统管理和应用性能优化中,监控进程的CPU、内存和IO使用率是非常重要的任务,下面我们就来讲讲如何Python写一个简单使用的监控进程的工具吧... 目录准备工作监控CPU使用率监控内存使用率监控IO使用率小工具代码整合在计算机系统管理和应用性能优化中,监

Kotlin Map映射转换问题小结

《KotlinMap映射转换问题小结》文章介绍了Kotlin集合转换的多种方法,包括map(一对一转换)、mapIndexed(带索引)、mapNotNull(过滤null)、mapKeys/map... 目录Kotlin 集合转换:map、mapIndexed、mapNotNull、mapKeys、map