虚拟内存和内存映射


是这样的,在现代的操作系统中,一个进程能够使用的地址空间是2^32次方,即4GB;但是试问有多少人的机子主存(内存属于主存,而磁盘等属于辅存)有这样的配置?现代的操作系统依赖于虚拟内存的计数。地址空间是被分为页面的集合(在物理内存中,“页”的说法被特化为“页帧”以区分),虚拟内存管理器的调度算法是以页面为粒度的(在Linux内核中,默认是4K)。一个进程需要的空间假设是6个页面,而系统只分配给它4个(物理内存的)页帧,那么就需要页面调度算法,包括取,放置,替换三个典型操作。关于这方面的算法,有很多的探讨,本人水平有限,不做叙述。简单的过程可以是:当进程访问一个特定的页面时,如果进程的页帧集合中包含,则通过地址转换(在硬件上通过MMU,存储管理单元实现,注意,页面到页帧的映射是不特定的,因为访问顺序是随机的,而且因为算法(主要是替换算法)的不同而有不同的行为和性能表现);如果不包含,这是就会产生一个缺页错的中断,并且物理内存还有空闲帧,则从辅存中加载这一个页面到主存中,之后同上;如果物理内存已经满帧,则根据替换算法,卸载一个页面,加载需要的页面。
以上的讨论是基于可执行程序的,源程序中的所有标识符在编译成为可执行程序后,都会分配进程的虚拟地址;如果一条指令访问的变量不在目前加载的页面范围,就会引起缺页错。而且一个可执行程序分为几个片段,每个片段占据一个页面空间。一般是初始化代码执行过一次后将不需要访问该页面了,而某些关键片段,例如会有循环的反复访问,因此程序具有局部性原则。一个优秀的替换算法应该能够考虑到这一点。
利用分页实现IPC
假设要拷贝的信息刚好被加载到一个页面大小的缓冲中,通过在发送者的页表中删除指向包括该页页帧的页表指针,并将它增加到接受者的页表中,这样能够高性能的实现IPC。
写时拷贝技术也有助于高效的IPC。两个进程可以同时映射自己的某个页面的虚拟内存到同一个物理内存(但是对于它们,不一定是同样的帧)实现共享,如果发生了对该页面的改写,则拷贝该帧到该进程的自己页表中成为自己的私有页帧。
还有,在文件I/O中,一个开销是把主存中的页面写入到辅存中。通过设置脏位,在加载的时候,将该页面的脏位设清零,在改变其内容的时候置位,那么就可以避免不必要的写入开销。

下面讨论的是WIN32的,但是对Linux也同样有效。
引用:http://www.cppblog.com/ViskerWong/archive/2009/01/01/47582.html
 内存映射文件有三种,第一种是可执行文件的映射,第二种是数据文件的映射,第三种是借助页面交换文件的内存映射.应用程序本身可以使用后两种内存映射.

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()函数,在内部都使用内存映射文件技术.

使用内存映射文件的方法:
1.利用内存映射文件进行文件I/O操作:
  CreateFile()-->CreateFileMapping()-->MapViewOfFile()......

2.利用内存映射文件实现Win32进程间通信:
我只介绍两种常用的方法:
  第一种方法:两个进程使用同一个文件映射核心对象,打开各自的视图,或者父进程把自己创建的文件映射核心对象继承给子进程使用.这种方法比较安全有效.
  第二种方法:基于页面交换文件的内存映射对象.在调用CreateFileMapping()函数时,传递的文件句柄为0xFFFFFFFF,系统就从页面交换文件中提交物理内存,然后进程之间按照第一种方法进程通信.这种方法不用事先准备一个特殊的文件(也就是说不用事先调用CreateFile()返回一个文件的句柄),非常方便.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用中提到,共享内存是一种进程间通信的方式,允许不同进程将自己的虚拟地址映射到同一块物理地址上,从而共享同一段物理内存。这意味着多个进程可以直接访问和修改同一块内存空间中的数据,而不需要进行复制或者通过消息传递来进行通信。共享内存的使用可以提高进程间通信的效率和速度。 引用中提到,共享内存是通过内存映射机制来实现的。内存映射机制(mmap)是一种将文件或者其他对象映射到进程地址空间的方法。它允许进程将一个文件或者一段内存映射到自己的地址空间中,使得进程可以直接读写该文件或者内存,就像操作自己的内存一样。这样,不同进程可以通过共享同一块映射虚拟内存区域,实现共享内存的目的。 引用中进一步解释了共享内存的作用。由于不同进程之间的内存是互相独立的,无法直接操作对方的内存数据。但是通过操作系统提供的内存映射机制,可以将不同进程的一块地址空间映射到同一个虚拟内存区域上,从而实现不同进程之间共享一块内存的目的。 综上所述,共享内存是一种通过将不同进程的虚拟地址映射到同一块物理内存上来实现的进程间通信方式。这种方式是通过内存映射机制来实现的,使得不同进程可以直接读写和修改同一块内存空间中的数据,从而提高了进程间通信的效率和速度。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值