数据共享,内存映射文件和虚拟内存,共享内存

一、内存映射

内存映射文件允许开发人员预定一块地址空间区域并给区域调拨物理存储器。内存映射文件的物理存储器来自磁盘已有的文件,而不是来自系统的页交换文件。一旦把文件映射到地址空间,就可以对它进行访问,就好像整个文件都已经被载入内存一样。不必再对文件执行I/O操作。

使用内存映射文件来颠倒文件内容时,先打开文件并向系统预订一块虚拟地址空间区域。接着让系统把文件的第一个字节映射到该区域的第一个字节,然后就可以访问这块虚拟内存区域,就好像它实际上包含了文件一样。优点:在于系统为我们处理所有与文件缓存有关的操作,不必再分配任何内存,把文件中的数据载入内存,把数据写回文件,以及释放内存。

内存映射文件有三种,第一种是可执行文件的映射,第二种是数据文件的映射,第三种是借助页面交换文件的内存映射.应用程序本身可以使用后两种内存映射.

过程

1.创建或打开一个文件内核对象。该对象标识了我们想要用作内存映射文件的那个磁盘文件。CreateFile().

2.创建一个文件映射内核对象来告诉系统文件的大小以及如何访问文件。CreateFileMapping()

3.告诉系统把文件映射对象的部分或者全部数据映射到进程的地址空间。MapViewOfFile();

4.从进程的地址空间撤销对文件数据的映射。UnmapViewOfFile();

5.关闭文件映射对象和文件对象。

数据共享,内存映射文件和虚拟内存,共享内存_第1张图片

数据的一致性

把同一文件中的数据映射到多个视图中,如果应用程序在一个视图中作了修改,那么系统会更新所有其他视图以反映修改后的内容。因为,该页面被多次映射到进程的虚拟地址空间,系统在同一个内存页面中保存被映射的数据。

若果多个进程把同一数据文件映射到多个视图,数据依然保持一致性。因为数据文件中的每个页面在内存中只有一份,但这些内存页面会被映射到多个进程的地址空间中。

若一个应用程序想要调用createfile来打开文件,但另外一个进程已经映射了同一个文件。为了防止一读一些ReadFile和WriteFile,可以设置为独占模式。

由于只读文件不存在一致性问题,非常适合内存映射文件,决不允许内存映射文件来跨网络共享可写文件,因为系统无法保证数据视图的一致性。如果一台机器更新了内容,另外一台机器无法知道。

1.可执行文件映射:
  Windows在执行一个Win32应用程序时使用的是内存映射文件技术.系统先在进程地址空间的0x00400000以上保留一个足够大的虚拟地址空间(0x00400000以下是由系统管理的),然后把应用程序所在的磁盘空间作为虚拟内存提交到这个保留的地址空间中去(我的理解也就是说,虚拟内存是由物理内存和磁盘上的页面文件组成的,现在应用程序所在的磁盘空间就成了虚拟地址的页面文件).做好这些准备后,系统开始执行这个应用程序,由于这个应用程序的代码不在内存中(在页面文件中),所以在执行第一条指令的时候会产生一个页面错误(页面错误也就是说,系统所访问的数据不在内存中),系统分配一块内存把它映射到0x00400000处,把实际的代码或数据读入其中(系统分配一块内存区域,把它要访问的在页面文件中的数据读入到这块内存中,需在注意是系统读入代码或数据是一页一页读入的),然后可以继续执行了.当以后要访问的数据不在内存中时,就可以通过前面的机制访问数据.对于Win32DLL的映射也是同样,不过DLL文件应该是被Win32进程共享的(我想应该被映射到x80000000以后,因为0x80000000-0xBFFFFFFF是被共享的空间).

  当系统在另一个进程中执行这个应用程序时,系统知道这个程序已经有了一个实例,程序的代码和数据已被读到内存中,所以系统只需把这块内存在映射到新进程的地址空间即可,这样不就实现了在多个进程间共享数据了吗!然而这种共享数据只是针对只读数据,如果进程改写了其中的代码和数据,操作系统就会把修改的数据所在的页面复制一份到改写的进程中(我的理解也就是说共享的数据没有改变,进程改写的数据只是共享数据的一份拷贝,其它进程在需要共享数据时还是共享没有改写的数据),这样就可以避免多个进程之间的相互干扰.

2.数据文件的内存映射:
  数据文件的内存映射原理与可执行文件内存映射原理一样.先把数据文件的一部分映射到虚拟地址空间的0x80000000 - 0xBFFFFFFF,但没有提交实际内存(也就是说作为页面文件),当有指令要存取这段内存时同样会产生页面错误异常.操作系统捕获到这个异常后,分配一页内存,映射内存到发生异常的位置,然后把要访问的数据读入到这块内存,继续执行刚才产生异常的指令(这里我理解的意思是把刚才产生异常的指令在执行一次,这次由于数据已经映射到内存中,指令就可以顺利执行过去),由上面的分析可知,应用程序访问虚拟地址空间时由操作系统管理数据在读入等内容,应用程序本身不需要调用文件的I/O函数(这点我觉得很重要,也就是为什么使用内存映射文件技术对内存的访问就象是对磁盘上的文件访问一样).

3.基于页面交换文件的内存映射:
  内存映射的第三种情况是基于页面交换文件的.一个Win32进程利用内存映射文件可以在进程共享的地址空间保留一块区域(0x8000000 - 0xBFFFFFFF),这块区域与系统的页面交换文件相联系.我们可以用这块区域来存储临时数据,但更常见的做法是利用这块区域与其他进程通信(因为0x80000000以上是系统空间,进程切换只是私有地址空间,系统空间是所有进程共同使用的),这样多进程间就可以实现通信了.事实上Win32多进程间通信都是使用的内存映射文件技术,如PostMessage(),SentMessage()函数,在内部都使用内存映射文件技术.

数据共享

在Windows中,在同一台机器上数据共享的最底层机制就是内存映射文件。
这种数据共享机制是通过让两个或多个进程映射同一个文件映射对象的视图来实现,意味着在进程间共享相同的物理存储页面。对多个进程共享同一个文件映射对象来说,所有进程使用的文件映射对象的名称必须完全相同。 《windows核心编程》 p473

虚拟内存

数据共享,内存映射文件和虚拟内存,共享内存_第2张图片

共享内存

<1>共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。

<2>为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。

<3>由于多个进程共享一段内存,因此也需要依靠某种同步机制。

实现

共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信。因为是通过内存操作实现通信,因此是一种最高效的数据交换方法。
1、调用 CreateFileMapping 创建一个文件映射内核对象;

通过这个API函数 将创建一个内存文件映射的内核对象,用于文件映射到内存。与虚拟内存一样,内存文件映射可以用来保留一个地址空间的区域,并将物理存储器提交给该区域。它们之间的差别是,物理存储器来自一个已经位于磁盘上的文件,而不是系统的页文件。

2、调用 MapViewOfFile 映射到当前进程的虚拟地址上;

将内存映射文件映射到进程的虚拟地址中

3、在接收进程中打开对应的内存映射对象

在数据接收进程中,首先调用OpenFileMapping()函数打开一个命名的文件映射内核对象,得到相应的文件映射内核对象句柄hFileMapping;如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,将文件映射内核对象hFileMapping映射到当前应用程序的进程地址,进行读取操作。(当然,这里如果用CreateFileMapping也是可以获取对应的句柄)

4.调用完之后,清理
//取消本进程地址空间的映射;
UnmapViewOfFile(pLocalMem);
//关闭文件映射内核文件
CloseHandle(hFileMapping);

你可能感兴趣的:(操作系统)