=====================Windows之内存映射文件=====================

几乎每个应用程序都要处理文件,但要处理好并不容易。应用程序到底是先应该打开文件、再读取文件,最后关闭文件呢,还是应该先打开文件,再用一个缓存算法来读取和写入

文件的不同部分?Windows为我们提供了一个两全其美的解决方案---内存映射文件。

内存映射文件允许开发人员预订一块地址空间区域并给区域调拨物理存储器。不同之处在于,内存映射文件的物理存储器来自磁盘上已有的文件,而不是来自系统的页交换文件。一旦把文件映射到地址空间,我们就可以对它进行访问,就好像整个文件都已经被载入内存一样。

内存映射文件的三种情况:

l       系统使用内存映射文件来载入并运行.exe和动态链接库dll文件。这大量节省了页交换文件的空间以及应用程序启动的时间。

 

l       开发人员可以用内存映射文件来访问磁盘上的数据文件。这可以避免直接对文件进行I/O操作和对文件内容进行缓存。

 

l       通过内存映射文件,可以在同一台机器的不同进程之间共享数据。Windows提供的一些进程间传送数据的方法都是通过内存映射文件来实现的。

======================================================================

映射到内存的可执行文件.exe和动态链接库DLL

当一个线程调用CreateProcess的时候,系统执行以下步骤。

(1)确定所制定的可执行文件所在的位置。如果无法找到该.exe文件,那么系统将不会创建进程,返回NULL。

(2)系统创建一个新的进程内核对象。

(3)系统为新进程创建一个私有地址空间。

(4)系统预订一块足够打的空间来容纳.exe。默认情况下.exe文件的基地址是0x0040 0000。只需在构建.exe文件时使用/BASE连接器开关就可以指定一个不同的基地址。

(5)系统会对地址空间区域进行标注,表明该区域的后备物理存储器来自磁盘上的.exe文件,而并非来自系统的页交换文件。

(6)当.exe文件映射到地址空间之后,会访问.exe文件中的一个段,这个短列出了一些DLL文件,他们包含.exe文件调用到的函数。然后系统会调用LoadLibrary来载入每个DLL。默认情况下DLL的基地址设置为0x1000 0000。同样可以在构建DLL时使用/BASE连接器开关就可以指定一个不同的基地址。

(7)系统会对地址空间区域进行标注,表明该区域的后备物理存储器来自磁盘上的DLL文件,而并非来自系统的页交换文件。

(8)如果因为某些原因系统无法将.exe文件和所需的DLL文件映射到地址空间区域,系统会先给用户显示一个对话框,然后释放进程的地址空间和进程对象。这时CreateProcess会返回FALSE,我们可以通过调用GetLastError来查询为什么无法创建进程。

(9)当所有的.exe和DLL文件都被映射到进程的地址空间之后,系统开始执行.exe文件的启动代码。系统会负责所有的换页,缓存以及高速缓存操作。如果.exe文件中的代码跳转到一个指定地址,但该地址尚未载入内存,那么会引发一个页面错误。系统会检测到这个错误并且自动将该页代码从文件影响载入到内存中。然后系统会把该内存页映像到进程地址空间中的适当位置,并让线程继续执行,就好象该页代码早已载入内存一样。当然,这一切对应用程序来说都是透明的。

第二个实例的地址空间

代码页面1

代码页面2

代码页面3

数据页面1

数据页面2

第一个实例的地址空间

代码页面1

代码页面2

代码页面3

数据页面1

数据页面2

虚拟内存

代码页面2

代码页面1

数据页面2

代码页面3

数据页面1

新页面

注意:

同一个可执行文件或DLL的多个实例不会共享静态数据。如果当实例1试图修改数据页面2中的一个全局变量时,系统会分配一个新的虚拟内存页,然后把数据页面2中的内容复制到新页面中。然后系统会更新第一个实例的地址空间,新的数据页面就回和原始数据页面一样,映射到进程地址空间中的同一位置。现在系统不仅可以让进程修改全局变量的值,也不用担心会修改到同一个应用程序的其它实例的数据了。

.exe和DLL文件映像的组成:

常用段属性

.bss : 未经初始化的数据段

.data : 已初始化的数据段

.reloc : 重定位表信息

.tls : 线程本地存储

.rdata : 只读的运行时数据

.debug : 调式信息

.text : .exe文件和DLL的代码

我们可以用编译器指示符来创建自己的段。

#pragma data_seg(“sectionname”)

LONG g_lInstanceCount = 0;       //必须已经初始化

#pragma data_seg()

======================================================================

映射到内存的数据文件

为什么要使用内存映射文件呢?我们来看一个颠倒文件内容的例子。我们可以用通常的办法,先把文件载入到内存中,当然既要考虑到内存空间的利用率又要考虑到磁盘空间的使用情况,而且还要考虑数据在写回过程中不会丢失,这看起来并不吓人但将会是比较困难的。

而如果使用内存映射文件,只需要打开文件并向系统预订一块虚拟地址空间。接着系统把文件的第一个字节映射到该区域中的第一个字节。然后便可以访问这个虚拟内存区域,就好象它实际上包含了文件一样。在这种情况下,直接调用C运行库函数_tscrev就能颠倒文件中的数据。这么做的好处是我们不必再分配任何内存,把文件中的数据载入内存,吧数据写回文件、以及释放内存块。但遗憾的是如果操作过程中断电仍然能导致数据被破坏。

如何使用内存映射文件呢?接下来将会看到创建的过程。

1.         创建或打开文件内核对象

HANDLE CreateFile(

PCSTR pszFileName,                             //要打开的文件名

DWORD dwDesiredAccess,                   //文件的访问权限

DWORD dwShareMode,                        //共享文件方式

PSECURITY_ATTRIBUTES psa,            //安全属性设置

DWORD dwCreateDisposition,               //创建设备部署

DWORD dwFlagsAndAttributes,             //文件属性标志

HANDLE hTemplateFile                          //模板文件句柄

);

调用CreateFile是为了告诉操作系统文件映射的物理存储器所在的位置。路径可以是文件在磁盘、网络或者光盘上所在的位置。

2.         创建文件映射内核对象

HANDLE CreateFileMapping(

HANDLE hFile,                                     //文件句柄,来自第一步生成的文件句柄

PSECURITY_ATTRIBUTES psa,            //安全属性设置

DWORD fdwProtect,                             //指定保护属性

DWORD dwMaximumSizeHigh,              //内存文件映射的最大大小字节数的高32位

DWORD dwMaximumSizeLow,              //低32位

PCTSTR pszName                                 //文件映射对象的名称

);

调用CreateFileMapping的作用是告诉系统文件映射对象需要多大的物理存储器。

3.         将文件的数据映射到进程的地址空间

HANDLE MapViewOfFile(

HANDLE hFileMappingObject,                //文件映射对象句柄

DWORD dwDesiredAccess,                   //文件的访问权限

DWORD dwFileOffsetHigh,                    //映射到视图中的第一个数据字节高32位

DWORD dwFileOffsetLow,                    //低32位

SIZE_T dwNumberOfBytesToMap          //数据文件的多少字节被映射到地址空间

);

这部分主要告诉系统两件事。

第一,   我们必须告诉系统应该把数据文件中的哪个字符映射到视图中的第一个字符。

第二,   我们必须告诉系统要把数据文件中的多少映射到地址空间中去。

函数的返回PBYTE pbFile指针,以后便可以直接对这个指针进行操作就像是对文件操作一样。

4.         从进程的地址空间撤销对文件数据的映射

BOOL UnmapViewOfFile(PVOID pvBaseAddress);

pvBaseAddress就是上一步中的pbFile。如果不这样做,在进程终止之前,区域都得不到释放。

如果需要将数据立即写回到磁盘中,可以调用以下函数

BOOL FlushViewOfFile(

PVOID pvAddress,

SIZE_T dwNumberOfBytesToFlush);

5.         关闭文件映像对象和文件对象

CloseHandle(hFileMapping);

CloseHandle(hFile);

如果不关闭句柄会引起资源泄漏。让我们做一个“合格”的程序员。

======================================================================

内存映射文件处理大文件

当处理大容量文件时,普通32位地址空间的操作无法满足,这时就需要使用内存映射文件了,一开始吧文件开头的部分映射到内存中。完成对文件的第一个视图的访问后,可以撤销对文件中一部分的映射,然后把另一部分映射到视图中。一直重复这个过程,知道完成对整个文件的访问。

Windows之内存映射文件相关推荐

  1. windows笔记-内存映射文件

    Windows提供了3种进行内存管理的方法: • 虚拟内存,最适合用来管理大型对象或结构数组. • 内存映射文件,最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行的多个进程之间共享数据. ...

  2. Windows使用内存映射文件

    1.简介 内存映射文件主要用于以下三种情况: 系统使用内存映射文件,以便加载和执行. exe和DLL文件.这可以大大节省页文件空间和应用程序启动运行所需的时间. 可以使用内存映射文件来访问磁盘上的数据 ...

  3. MongoDB内存映射文件

    2019独角兽企业重金招聘Python工程师标准>>> Mongodb源码分析--内存文件映射(MMAP) 内存映射文件原理探索 Linux 内存映射函数 mmap()函数详解 Li ...

  4. java 内存映射文件进程间通讯_[转]Windows环境下利用“共享内存”实现进程间通信的C/C++代码---利用CreateFileMapping和MapViewOfFile...

    进程间的通信方式有很多种, 上次我们说了最傻瓜的"共享外存/文件"的方法. 那么, 在本文中, 我们即将学习"共享内存"的方式实现进程间的通信, 这是IPC最快 ...

  5. Windows核心编程 第十七章 -内存映射文件(下)

    17.3 使用内存映射文件 若要使用内存映射文件,必须执行下列操作步骤: 1) 创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存映射文件的文件. 2) 创建一个文件映射内核对象,告诉系统该 ...

  6. Windows核心编程 第十七章 -内存映射文件(上)

    第1 7章 内存映射文件 对文件进行操作几乎是所有应用程序都必须进行的,并且这常常是人们争论的一个问题.应用程序究竟是应该打开文件,读取文件并关闭文件,还是打开文件,然后使用一种缓冲算法,从文件的各个 ...

  7. 《windows核心编程》 17章 内存映射文件

    内存映射文件主要用于以下三种情况: 系统使用内存映射文件载入并运行exe和dll,这大量节省了页交换文件的空间以及应用程序的启动时间 开发人员可以使用内存映射文件来访问磁盘上的数据文件.这使得我们可以 ...

  8. 内存映射文件——Windows核心编程学习手札之十七

    内存映射文件 --Windows核心编程学习手札之十七 与虚拟内存一样,内存映射文件保留地址空间,并将物理存储器提交给该区域,差别在于所提交的物理存储器是磁盘上有文件存在的空间,而非系统的页文件,一旦 ...

  9. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

最新文章

  1. 手机黑产为啥没支付宝的份?官方回应:犯罪分子无法突破人脸识别
  2. git 出现 fatal: refusing to merge unrelated histories 错误
  3. 群晖套件来源_群晖安装IPKG包管理器及第三方社区安装包步骤
  4. 怎么把linux虚拟机硬盘扩容,Windows下虚拟机Linux(CentOS8)扩容设置 - 磁盘扩容中的坑和解决方法...
  5. Sql Server系列:键和约束
  6. 卢伟冰宣布Redmi新机即将发布 疑为Redmi 8A
  7. Spring boot 连接 sqlserver
  8. Python selenium chrome 环境配置
  9. vue全局组件中再创建多个组件
  10. AltiumDesigner画图不求人12 AD库转换为PADS库
  11. 佳能打印机IP1880,打印提示墨盒收集器已满的解决方法
  12. dsp2812 c语言数据类型长度,DSP2812代码长度超出RAM容量,有谁遇到过吗?
  13. 关于C语言中有符号的整数值范围为什么是从-32768~32767以及有符号和无符号区别
  14. hadoop2.9安装及配置_阿里云服务器上装Hadoop的心得(内附Hadoop2.9.2详细安装教程)...
  15. esaywechat 微信公众号jsapi支付
  16. 视频教程-AI 教程illustrator从入门到精通-Illustrator
  17. java实现超级玛丽游戏
  18. 洛谷P5707 上学迟到问题
  19. 怎么把视频转换成mp3格式?
  20. 【JavaWeb】Http请求报文详解

热门文章

  1. json字符串转成 json对象 json对象转换成java对象
  2. linuxoracle静默安装应答文件修改_Linux7静默安装Oracle11g教程,亲测实用有效!
  3. 数据结构-中序遍历二叉树(基于C++)
  4. 计算机网络笔记—计算机网络概述
  5. Self6D: Self-Supervised Monocular 6D Object Pose Estimation论文翻译
  6. 爬虫实战 | 手把手用Python教你采集可视化知乎问题的回答(内附代码)
  7. 随机生成一个有向无环图
  8. 学计算机买什么笔记本好点,学设计的用什么笔记本好呢
  9. python保存requests请求的文件的实战代码
  10. h5 cookie获取和设置