Windows API笔记(六)内存映射文件
Windows API笔记(一)内核对象
Windows API笔记(二)进程和进程间通信、进程边界
Windows API笔记(三)线程和线程同步、线程局部存储
Windows API笔记(四)win32内存结构
Windows API笔记(五)虚拟内存
Windows API笔记(六)内存映射文件
Windows API笔记(七)堆
Windows API笔记(八)文件系统
Windows API笔记(九)窗口消息
Windows API笔记(十)动态链接库
Windows API笔记(十一)设备I/O
文章目录
- 1. 内存映射exe和dll
- 1.1 不被exe或dll的多个实例共享的静态数据
- 2. 内存映射数据文件
- 2.1 方法1:一个文件,一个缓冲区
- 2.2 方法2:两个文件,一个缓冲区
- 2.3 方法3:一个文件,两个缓冲区
- 2.4 方法4:一个文件,零个缓冲区
- 3. 使用内存映射文件
- 3.1 第1步:创建或打开文件内核对象
- 3.2 第2步:创建文件映射内核对象
- 3.3 第3步:将文件数据映射入进程地址空间
使用内存映射文件主要有3个目的:
- 系统使用内存映射文件来装入和执行exe和dll文件。这大大节省了页面文件空间和弃用程序开始执行的时间。
- 可以使用内存映射文件来访问磁盘上的数据文件,无需进行文件I/O操作或缓冲文件的内容。
- 可以使用内存映射文件来允许运行在同一计算机上的多个进程之间共享数据。(Win32还提供了其他方法在进程间进行数据通信,但这些方法的实现都使用了内存映射文件)。
1. 内存映射exe和dll
当线程调用CreateProcess时,系统执行下列步骤:
系统定位在CreateProcess中指定的exe文件。如果找不到,就不会创建进程,CreateProcess返回FALSE。
系统创建一个新的进程内核对象。
系统为新进程创建一个4GB的地址空间。
系统在地址空间中保留了足够装下exe文件的一块区域。该区域的位置是在exe文件中指定的。默认,exe文件的基本地址是0x00400000。不过,在链接程序时,可以使用链接器的/BASE选项来重载这一地址。
系统注意到支持该保留区域的物理存储是磁盘上的exe文件,而不是系统的页面文件。在exe文件被映射到进程的地址空间之后,系统访问exe文件里的某一节,那里列出了包含exe调用的函数的dll文件。然后系统为每个dll调用LoadLibrary,如果某个dll还需要其他的dll,系统也会调用LoadLibrary来加载这些dll。每当调用LoadLibrary来加载一个dll时,系统执行蕾仕于上面的第4和5步的行动:
- 系统保留一块足够装得下dll文件的区域。该区域的位置是dll文件自己指定的。缺省时,vc++使得dll的基本地址为0x10000000。不过,在建立dll时,可以使用链接器的/BASE选项来重设该值。
- 如果系统不能再dll指定的基本地址处保留区域,或者是因为该区域被其他dll或exe占据了,或者是因为该区域不够大,系统就将在地址空间寻找另一块区域来保留给该dll。如果dll不能加载到它指定的基本地址是很不幸的,这有两个原因。首先,如果该dll不含有修正信息,系统就可能不能加载该dll。(在创建dll时,可以使用链接器的/FIXED开关来删除修正信息。这能使dll文件变小,但也意味着该dll必须加载到它指定的地址。)其次,系统必须在dll内部进行一些重定位。在Windows NT上,这些重定位需要系统的页面文件中的一些额外存储;这还增加了加载dll所需的时间。
- 系统会记下来支持保留区域的文件存储是在磁盘上的dll文件而不是在系统的页面文件中。如果因为dll不能加载到它指定的基本地址,Windows NT必须进行重定位的话,系统也会记下来该dll的一些物理存储被映射到了页面文件中。
如果系统因故不能映射exe和所需的dll,CreateProcess将向调用者返回FALSE,可以调用GetLastError来弄清进程为什么不能被创建。
在所有的exe和dll文件被映射进进程的地址空间之后,系统就能开始执行exe文件的启动代码了。在exe我呢见被映射之后,系统会负责所有的页面、缓冲和缓存。例如,如果exe中的代码跳到了还没有装入内存的一条指令的地址,会产生一个错误。系统检测到错误后,会自动把该代码所在页从我呢见的映象装入到RAM的页中。而后系统把RAM页映射到进程地址空间中的正确位置。允许线程继续执行,就好像代码页早已被装入一样。当然,这些对程序是不可见的。每当进程中的线程试图访问的数据或代码没有被装入RAM时,就会重复该过程。
1.1 不被exe或dll的多个实例共享的静态数据
系统通过使用该内存管理系统和写拷贝特性来防止某一实例改变了共享的静态数据导致所有实例的内存内容都被改变。每当应用试图写它的内存映射文件时,系统捕捉到请求,对包含有被写的内存的页分配一块新内存,拷贝页的内容,然后允许应用程序写这块新分配的内存。这样,其他的实例就不会受到影响
可以在exe或dll中创建能被所有实例共享的全局遍历。简单的说,该方法要求使用#pragma data_seg()编译器指令将要共享的变量放在它们自己的节中。然后必须使用/SECTION: name,attributes开关来告诉链接器要让该节中的数据被所有的实例或文件的映象共享。
2. 内存映射数据文件
在exe或dll文件被加载时,操作系统自动使用前一节中讲述过的技术。不过,还可能在进程的地址空间中映射一个数据文件。这使得操纵大数据流非常方便。
为了理解这样使用内存映射文件的强大功能,让我们看一下实现一个程序将文件中的所有字节倒放的4中可能的方法。
2.1 方法1:一个文件,一个缓冲区
流程:
- 申请一块足够大的内存
- 将文件读入内存
- 倒置内存中的数据
- 写入文件
缺点:
- 必须分配一块与文件大小相同的内存,如果文件较大(超过内存限制)就可能无法实现
- 写回文件时,如果过程被中断了,文件的内容就被破坏了
2.2 方法2:两个文件,一个缓冲区
流程:
- 创建一个新文件
- 创建一个较小的内部缓冲区,比如8KB
- 读入源文件的最后8KB至内部缓冲区
- 倒置内部缓冲区的内容,然后写入新文件
- 重复3-4,直至读完源文件
- 删除源文件,保留新文件
缺点:
- 比方法1复杂
- 处理速度比方法1要慢
- 可能要占用巨大的硬盘空间
2.3 方法3:一个文件,两个缓冲区
流程:
- 申请两个8KB的内存缓冲区
- 将文件的开始8KB字节读入一个缓冲区
- 将文件的最后8KB字节读入另一个缓冲区
- 分别倒置两个缓冲区的数据,然后分别写入文件的开始和结尾
- 重复2-4,只至文件全部读完
优点:
- 节省内存和硬盘空间
缺点:
- 实现复杂
- 处理过程被打断则可能破坏源文件
2.4 方法4:一个文件,零个缓冲区
使用内存映射文件倒置文件内容。
流程:
- 将文件映射到虚拟地址空间
- 调用_strrev将文件中的数据倒置即可
优点:
- 系统替你管理所有的文件缓存,不必分配任何内存,不必将文件装入内存,页不必将文件写回文件和释放任何内存块
缺点:
- 掉电等意外事故可能在处理过程中破坏源文件
3. 使用内存映射文件
要使用内存映射文件,必须执行下列3步:
- 创建或打开一个文件内核对象来标识硬盘上的想用作内存映射文件的文件
- 创建一个文件映射内核对象来告诉系统文件的大小和想要如何访问文件
- 告诉系统把文件映射对象的全部或部分映射到进程的地址空间中
使用完内存映射文件后,必须执行下列3步进行清理工作:
- 告诉文件把文件映射对象从进行的地址空间中解除映射
- 关闭文件映射内核对象
- 关闭文件内核对象
3.1 第1步:创建或打开文件内核对象
调用CreateFIle创建或打开文件内核对象:
HANDLE
CreateFileA(_In_ LPCSTR lpFileName,_In_ DWORD dwDesiredAccess,_In_ DWORD dwShareMode,_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,_In_ DWORD dwCreationDisposition,_In_ DWORD dwFlagsAndAttributes,_In_opt_ HANDLE hTemplateFile);
dwDesiredAccess 以何种方式访问文件,取值范围:
值 | 含义 |
---|---|
0 | 不能读写文件的内容 |
GENERIC_READ | 可读 |
GENERIC_WRITE | 可写 |
GENERIC_READ|GENERIC_WRITE | 可读可写 |
对于内存映射文件,必须以只读或读写方式打开文件。
dwShareMode 如何共享该文件,取值范围:
值 | 含义 |
---|---|
0 | 不共享,独占文件 |
FILE_SHARE_READ | 读共享,其他以带有写的方式打开文件都会失败 |
FILE_SHARE_WRITE | 写共享,其他以带有读的方式打开文件都会失败 |
FILE_SHRE_READ|FILE_SHARE_WRITE | 读写共享,其他打开文件的尝试都会成功 |
3.2 第2步:创建文件映射内核对象
调用CreateFile是告诉操作系统文件映射的物理存储的位置。传送的路径名指出了支持文件映射的物理存储的确切位置,是在硬盘上、网络上、CD_ROM盘上等等。现在,必须告诉系统文件映射对象需要多少物理存储。这时调用CreateFileMapping:
HANDLE
CreateFileMappingA(_In_ HANDLE hFile,_In_opt_ LPSECURITY_ATTRIBUTES lpFileMappingAttributes,_In_ DWORD flProtect,_In_ DWORD dwMaximumSizeHigh,_In_ DWORD dwMaximumSizeLow,_In_opt_ LPCSTR lpName);
第1个参数hFile标识了想要映射到进程的地址空间中的文件的句柄。该句柄是由CreateFile创建的。
当创建文件映射对象时,系统并不保留地址空间中的区域,并把文件的内存映射到该区域。不过,当系统要向进程的地址空间映射存储时,系统必须知道赋给物理存储页的保护属性。dwProtect允许指定保护属性,大多数时候指定的是下表中给出的3个保护属性之一:
保护属性 | 含义 |
---|---|
PAGE_READONLY | 文件映射对象为只读,必须向CreateFile传递GENERIC_READ |
PAGE_READWRITE | 文件映射对象为可读可写,必须向CreateFile传递GENERIC_READ|GENERIC_WRITE |
PAGE_WRITECOPY | 文件映射对象为写拷贝,可读可写;写时会创建一份页面的私有拷贝。必须向CreateFile传递GENERIC_READ或GENERIC_READ|GENERIC_WRITE |
dwMaximumSizeHigh和dwMaximumSizeLow是最重要的参数。因为该函数的主要目的是确保对于文件映射对象有足够的物理存储。这两个参数告诉系统文件的最大字节大小。使用两个32位值是因为Win32支持64位的文件大小,dwMaximumSizeLow指定低32位,dwMaximumSizeHigh指定高32位。对于4GB以下的文件,dwMaximumSizeHigh总是为0。
如果在调用CreateFileMapping时传递PAGE_READWRITE标志,系统将会确保在磁盘上的相关数据文件的大小至少是由dwMaximumSizeHigh和dwMaximumSizeLow所给出的大小。如果文件比指定大小要小,将会增大磁盘上的文件。
#include <Windows.h>int main()
{// 创建新文件HANDLE hFile = CreateFile("MMFTest.dat",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);// 将使文件大小为100byteHANDLE hFilemap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,100,NULL);CloseHandle(hFilemap);CloseHandle(hFile);return 0;
}
最后一个参数lpName是一个字符串,用于给文件映射对象命名。该名字是用来与其他进程共享该对象的。不需要共享时,该参数一般为NULL。
3.3 第3步:将文件数据映射入进程地址空间
在创建文件映射对象后,还要让系统保留一块地址空间区域,将文件数据作为物理存储提交到该空间。这通过调用MapViewOfFile来实现:
LPVOID
MapViewOfFile(_In_ HANDLE hFileMappingObject,_In_ DWORD dwDesiredAccess,_In_ DWORD dwFileOffsetHigh,_In_ DWORD dwFileOffsetLow,_In_ SIZE_T dwNumberOfBytesToMap)
Windows API笔记(六)内存映射文件相关推荐
- 数字图像处理 使用C#进行图像处理六 内存映射文件
一.内存映射文件概述 不论32位系统还是64位系统,机器内存毕竟是有限的,如果要处理大文件的话,不可能全部加载到内存中进行处理,所以就需要用到内存映射文件. 内存映射文件将文件的内容映射到应用程序的逻 ...
- windows笔记-内存映射文件
Windows提供了3种进行内存管理的方法: • 虚拟内存,最适合用来管理大型对象或结构数组. • 内存映射文件,最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行的多个进程之间共享数据. ...
- 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...
- java 内存映射文件进程间通讯_[转]Windows环境下利用“共享内存”实现进程间通信的C/C++代码---利用CreateFileMapping和MapViewOfFile...
进程间的通信方式有很多种, 上次我们说了最傻瓜的"共享外存/文件"的方法. 那么, 在本文中, 我们即将学习"共享内存"的方式实现进程间的通信, 这是IPC最快 ...
- 内存映射文件——Windows核心编程学习手札之十七
内存映射文件 --Windows核心编程学习手札之十七 与虚拟内存一样,内存映射文件保留地址空间,并将物理存储器提交给该区域,差别在于所提交的物理存储器是磁盘上有文件存在的空间,而非系统的页文件,一旦 ...
- windows 内存映射文件
4.内存管理机制--内存映射文件 (Map) 和虚拟内存一样,内存映射文件可以用来保留一个进程地址区域:但是,与虚拟内存不同,它提交的不是物理内存或是虚拟页文件,而是硬盘上的文件. ·使用场合 ...
- Windows核心编程——》第十七章 内存映射文件 (Memory-Mapped Files)
1.概览 (1)什么是内存映射文件 内存映射文件是由一个文件到一块内存的映射,使进程虚拟地址空间的某个区域与磁盘上某个文件的部分或全部内容的建立映射. 建立映射后,通过该区域可以直接对被映射的磁盘文件 ...
- Windows核心编程 第十七章 -内存映射文件(下)
17.3 使用内存映射文件 若要使用内存映射文件,必须执行下列操作步骤: 1) 创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存映射文件的文件. 2) 创建一个文件映射内核对象,告诉系统该 ...
- Windows核心编程 第十七章 -内存映射文件(上)
第1 7章 内存映射文件 对文件进行操作几乎是所有应用程序都必须进行的,并且这常常是人们争论的一个问题.应用程序究竟是应该打开文件,读取文件并关闭文件,还是打开文件,然后使用一种缓冲算法,从文件的各个 ...
- 《windows核心编程》 17章 内存映射文件
内存映射文件主要用于以下三种情况: 系统使用内存映射文件载入并运行exe和dll,这大量节省了页交换文件的空间以及应用程序的启动时间 开发人员可以使用内存映射文件来访问磁盘上的数据文件.这使得我们可以 ...
最新文章
- 自己写的一个启动JBoss服务器的bat批处理
- python画柱形图-python绘制双柱形图代码实例
- iOS-生成国际化包-配置App多语言支持
- Python之tkinter:动态演示调用python库的tkinter带你进入GUI世界(Button展示图片事件)
- ac算法 有什么用 Java_AC算法使用例子
- 前端学习(2811):小程序学习之学习目录
- Jquery获取表格tr对象,并循环获取表格内容
- C#二进制方式(binary、varbinary、blob、longblog等)读写mysql
- 【C++】算法集锦(5):BFS算法
- 苹果开发者账号注册-您在注册时提供的地址无效或者不完整
- 水平居中和transform: translateY(-50%) 实现元素垂直居中效果
- linux mint 17 输入法,LinuxMint17.1 Rebecca中安装设置输入法
- 统计候选人得票(结构体)
- 对项目成本和进度的监控----挣值分析
- g33k 专用:使用 Mutt Email 客户端管理你的 Gmail
- UE4(虚幻4)预算上的纹理流送池(texture streaming poor over)报警解决方法
- 小说里的编程 【连载之三十五】元宇宙里月亮弯弯
- ArcGIS插件-太乐地图
- 达州市公安局探索IT运维新领域 北塔软件BTSO支撑“金盾工程”高效运转
- 职称计算机考试IE浏览器的使用