目录
  1. 1. 本文能学到的知识
  2. 2. 虚拟内存的由来
  3. 3. 物理内存和虚拟内存
  4. 4. 虚拟内存和物理内存的对应关系
  5. 5. 扩展
  6. 6. 结束语
你真的了解虚拟内存和物理内存吗

相信很多人都知道计算机有虚拟内存和物理内存,那么你真的能分清楚虚拟内存和物理内存吗?你知道为什么会出现虚拟内存吗?虚拟内存和物理内存之间的关系你又知道吗?

本文能学到的知识

  在文章开始之前,先说下阅读本文后能学到的知识。

  • 虚拟内存的由来。
  • 虚拟内存和物理内存的关系。
  • 虚拟内存的作用。

如果你已经掌握了这些知识,那么就不用继续阅读了,如果这些知识你都不熟悉或者不太清楚的话,那就继续阅读,从文章中来获取答案。

虚拟内存的由来

  为什么会出现虚拟内存呢?这就要从最初的操作系统来说起了,最初的操作系统并没有现在那么完善,刚开始的时候,程序是直接装载到物理内存中的。这就导致了下面的一些问题:

  1. 程序编写困难。
  2. 修改内存数据导致程序崩溃。

  先说问题1、为什么会导致程序编写困难呢?因为,操作系统是同时运行好多程序的,编写的程序是直接操作物理内存的,编写的时候就要考虑,自己的程序操作的内存地址,是否已经被其他程序占用了,如果被占用了的话,就要重新编写程序,重新安排程序的操作地址。

  再看问题2。因为是直接操作物理内存,这就意味着一个程序可以操作内存中的所有地址,如果有恶意程序修改了其他程序在用的地址中的数据,这就可能导致其他程序崩溃。

虚拟内存概念的出现就解决了上面的问题,虚拟内存的概念出现后,程序的编写就不再直接操作物理内存了,对每个程序来说,它们就相当于拥有了所有的内存空间,可以随意操作,就不用担心自己操作的内存地址被其他程序占用的问题了。同时,因为程序操作的是虚拟内存地址,这样就不会出现因为修改了其他应用程序内存地址中的数据而导致其他应用程序崩溃的问题了。

  这时,你可能会问,CPU都是操作物理内存的,这虚拟内存怎么和物理内存对应呢?问题很好,下文会给你答案。

物理内存和虚拟内存

  这里有必要说下物理内存和虚拟内存的概念,可能有的读者分的不太清楚。

  • 物理内存: 真实的内存,就是我们常说的那个4G、8G、16G的内存条。
  • 虚拟内存: 是一个概念,并不是实际的内存,对于4G内存的Linux系统来说,虚拟内存也为4G,其中1G为系统的内存,剩下的3G为应用程序的内存。


解读一下这张图,就是有1G虚拟内存是编写的应用程序操作不到的,应用程序最多只能操作3G的虚拟内存空间。

虚拟内存和物理内存的对应关系

  有计算机基础的人应该都知道,计算机的CPU操作的是物理内存的地址,而现在编写的程序操作的都是虚拟内存地址,那么虚拟内存怎样和物理内存对应起来的呢?这就涉及到一些操作系统的知识了,虚拟内存和物理内存都有分页的概念,一般一页是4096个字节,现在的操作系统虚拟地址和物理地址建立对应关系采用的是页映射的方式

页映射是虚拟存储机制的一部分,它随着虚拟存储的发明而诞生。页映射不是一下子就把程序的所有数据和指令都装入内存,而是将内存和所有磁盘中的数据和指令按照“页(Page)”为单位划分成若干个页,以后所有的装载和操作的单位就是页。以目前的情况,硬件规定的页的大小有4 096字节、8 192字节、2 MB、4 MB等,最常见的Intel IA32处理器一般都使用4 096字节的页,那么512 MB的物理内存就拥有512 * 1024 * 1024 / 4 096 = 131 072个页。

假设我们的32位机器有16 KB的内存,每个页大小为4 096字节,则共有4个页,
| 页编号 | 地址 |
| —— | ———————- |
| F0 | 0x00000000——0x00000FFF |
| F1 | 0x00001000——0x00001FFF |
| F2 | 0x00002000——0x00002FFF |
| F3 | 0x00003000——0x00003FFF |

假设程序所有的指令和数据总和为32 KB,那么程序总共被分为8个页。我们将它们编号为P0~P7。很明显,16 KB的内存无法同时将32 KB的程序装入,那么我们将按照动态装入的原理来进行整个装入过程。如果程序刚开始执行时的入口地址在P0,这时装载管理器(我们假设装载过程由一个叫装载管理器的家伙来控制,就像覆盖管理器一样)发现程序的P0不在内存中,于是将内存F0分配给P0,并且将P0的内容装入F0;运行一段时间以后,程序需要用到P5,于是装载管理器将P5装入F1;就这样,当程序用到P3和P6的时候,它们分别被装入到了F2和F3,它们的映射关系如图所示。

扩展

  可能上面的内容你还意犹未尽,那就再来简单的描述一下,Linux是怎样装载可执行程序的.首先,操作系统是会为一个可执行程序分配一个进程的,然后装载相应的可执行文件并执行。当有虚拟内存存在的情况下,上面的过程就要做三件事:

  1. 创建一个独立的虚拟地址空间。
  2. 读取可执行文件头,并建立可执行文件与虚拟空间的映射。
  3. 将CPU的指令寄存器设置成可执行文件的入口地址,启动运行。

首先是创建虚拟地址空间。创建一个虚拟空间实际上并不是创建空间而是创建映射函数所需要的相应的数据结构。

读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系。为什么要建立映射关系呢?因为,当程序执行发生页错误时,操作系统将从物理内存中分配一个物理页,然后将该“缺页”从磁盘中读取到内存中,再设置缺页的虚拟页和物理页的映射关系,这样程序才得以正常运行。但是很明显的一点是,当操作系统捕获到缺页错误时,它应知道程序当前所需要的页在可执行文件中的哪一个位置。这就是虚拟空间与可执行文件之间的映射关系。

再来看第3步,其实仔细思考第三步是会发现一些问题的,这里的可执行文件的入口地址是虚拟内存地址,那么就可能存在两个可执行程序的虚拟入口地址相同的问题,这个问题怎么解决呢?这时一个叫做“内存管理单元(Memory Management Unit)”简称“MMU”的硬件就诞生了,它的作用之一就是地址翻译,将虚拟地址翻译成物理地址,可以看下图,加深理解

简单总结一下这部分的内容,当操作系统装载一个可执行程序时,会首先创建虚拟地址空间,这个地址空间实际上就是一个数据结构;然后建立可执行文件与虚拟地址的映射,映射的作用就是知道虚拟空间对应可执行文件的哪个位置;最后就是将CPU的指令寄存器设置成可执行文件的入口地址,开始执行程序,程序开始执行的时候是会到入口地址那里找数据或程序执行,如果入口地址没有程序或数据,就会产生缺页中断,然后将虚拟地址对应可执行文件中的部分装载到物理内存中,再将这块物理内存和虚拟内存建立映射。

结束语

  本文主要讲解了虚拟内存有关的知识、简单的讲解了一下虚拟内存和物理内存之间的映射以及程序装载的过程,希望大家阅读后对虚拟内存能有更深的理解。

本文已由公众号“AndroidShared”首发

"扫码关注公众号,回复“获取资料"有惊喜"

文章作者: wizardev
文章链接: http://pi.wizardev.com:88/blog/%E4%BD%A0%E7%9C%9F%E7%9A%84%E4%BA%86%E8%A7%A3%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98%E5%92%8C%E7%89%A9%E7%90%86%E5%86%85%E5%AD%98%E5%90%97/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 wizardev的博客

评论