WINDOWS内存映射文件原理分析+代码演示

来源:互联网 发布:伊万的童年 知乎 编辑:程序博客网 时间:2024/06/18 09:41

WINDOWS为任何一个进程都赋予了4G的独立的虚拟地址空间,然而学过OS的都晓得这个只是逻辑上的虚拟的地址空间,进程实际拥有的远小于4GB的物理地址空间。并且进程的虚拟地址空间是每个进程私有的,有操作系统分配,本质上不过是物理内存地址的映像罢了。因此可以得出结论:在进程内运行的线程只能访问其所处进程的内存空间,即不同进程中可以用相同地址的指针用来指向属于各自进程中的内容,互不干扰,因为彼此都是以进程中的虚地址去访问内存的,操作系统再将虚拟地址转换为真正的物理内存地址。这样减少了程序员很多的麻烦事,使得我们而已在一个足够大的空间内自由随便的去做我们自己的功能,不必考虑会与其他进程发生冲突等。正是由于OS有如此完美的功能,我们才得以在我们自己的代码中不用顾忌太多,想申请空间就申请,不用关心这个空间是否被其他进程所占用?是否物理内存空间不够用(自古物理内存都是兵家必争之地嘛)等等一些让人头痛的问题。

其实上述所说的就是操作系统的内存管理模式,也是OS的一个核心功能吧。上面简单的几句只是概括,想弄的清清楚楚,那就得去看看操作系统原理方面的书喽,个人看的是计算机的心智---操作系统之哲学原理(简单评论一下此书:每篇都有一个简单的哲学引子,内容还算凑合吧)。

 

内存映射文件的使用

文件操作是一个基本功能,当然WINDOWS提供了很多具备这些功能的API,CreateFile(),WriteFile()等一大坨,对于小型体积的文件来说没什么问题,对于几十M,几百M甚至几G又或者更多的文件,这些API就显得力不从心了,好比那个东西。。。大家懂的。因此微软给我们提供了内存映射文件的技术。至于优点吗,肯定是速度快了,最后写个例子来证明,当然其他优点也很多了,可以去看看核心编程那本书。

 

使用流程

1

HANDLE WINAPI CreateFile(
  __in          LPCTSTR lpFileName,
  __in          DWORD dwDesiredAccess,
  __in          DWORD dwShareMode,
  __in          LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  __in          DWORD dwCreationDisposition,
  __in          DWORD dwFlagsAndAttributes,
  __in          HANDLE hTemplateFile
);
参数说明:大家都懂,MSDN。

2

HANDLE WINAPI CreateFileMapping(  __in          HANDLE hFile,  __in          LPSECURITY_ATTRIBUTES lpAttributes,  __in          DWORD flProtect,  __in          DWORD dwMaximumSizeHigh,  __in          DWORD dwMaximumSizeLow,  __in          LPCTSTR lpName);

参数还是看MSDN吧

这个函数的作用其实是创建一个文件映射内核对象,参数中指定了要占用多少空间,至于这个大家可以这么理解,这个函数的作用其实就是在进程的虚拟地址空间中分配一个指定大小的空间,这个空间的作用下面会说明。返回是一个句柄。

3

LPVOID WINAPI MapViewOfFile(
  __in          HANDLE hFileMappingObject,
  __in          DWORD dwDesiredAccess,
  __in          DWORD dwFileOffsetHigh,
  __in          DWORD dwFileOffsetLow,
  __in          SIZE_T dwNumberOfBytesToMap
);
参数:MSDN

函数作用:将文件中的数据(可以理解成CreateFile打开或者创建的那个文件中的数据)映射到进程地址空间中,疑问:映射多少个空间呢?这个问题在第二部已经解决了,也就是上面说的那个空间,其实就是为这步打基础的。这个函数执行之后,我们可以想象一下是什么情况:

进程的地址空间中现在已经有了一个文件的映射内容,大家都叫这个VIEW视图,这个视图跟在磁盘上的文件建立了映射关系,所以我们就不需要再打开磁盘文件,读取文件。。。。等一系列麻烦的操作了,直接操作这个视图完全OK了,因此此时的情况已经相当于把文件放到了进程地址空间中,如何操作呢,看看此函数的返回值就晓得了,返回的正好就是这个空间中的一个指针。是不是很巧妙。。。。当然了此时对这个视图操作会非常方便,非常简单,速度非常快。。。。。

 

关键的步骤就是上面三步,每步之间都有关系的,整体理解起来也不是很费劲吧。当然需要一点基础就是进程的虚拟地址空间的理解,这个是一个抽象,其实说真的也不太好说,意会吧。

4

BOOL WINAPI UnmapViewOfFile(
  __in          LPCVOID lpBaseAddress
);
解除映射关系

5

CloseHandle()

关闭句柄

6

CloseHandle()

同样关闭句柄

 

因为返回了2次handle。

关于效率问题,下面这个例子可以反映的很透彻的

用内存文件映射时间打开是47ms左右(我就运行了2次,系统是XP SP3 环境 vc 6)

普通的文件操作大概在6593ms左右(也是测试了2次,同上)

效率差别之明显,不亚于国内的贫富差距吧。呵呵,不谈政治问题。

test code:

 

原创粉丝点击