本文转载自 https://blog.csdn.net/robinsongsog/article/details/46646557
在同一台机器上面,不同的进程共享着系统内存和CPU, 随着对CPU计算资源的需求越来越大,程序只是会变慢,但是如果内存不够,可能有些程序就完全没法运行了,另外,内存也是非常脆弱的, 如果一个进程不小心,写到另外一个进程用到的内存,就会导致另一个进程莫名其妙的错误,因为这种错误完全与程序逻辑没有关系半毛钱关系。
为了使内存管理更为高效,还有少出错误,现代计算机系统提供了一个抽象的主存叫 虚拟内存(virtual memory) . 虚拟内存是硬件地址翻译部件,硬件异常处理机制,主存,磁盘文件,内核软件,完美合作的成果。 虚拟内存为每一个进程提供了一个够大的,统一的,私有的地址空间。在一个非常清晰的机制下面,虚拟内在提供了三种重要功能:
- 将主存作为磁盘文件的cache. 存放在主存上面的仅仅是用得着的,活跃部分。只有在有需要的时候,将数据从磁盘搬到内存上,或者换回去
- 它简化了内存管理,为每一个进程提供统一的地址空间
- 它保护了一个进程的地址空间不被其他进程破坏
对于计算机系统来说,虚拟内存是一个非常伟大的想法,主要就是,他无声无息在后台运行,不需要写应用程序的程序员任何干预,既然虚拟内存在后台工作得这么出色,那程序员为什么还要去理解它? 主要是这么几个原因
- 虚拟内存在计算机系统各个层次都扮演了一个核心角色,编译器,链接器(linker), 载入器(loader) ,文件, 进程 都离不开虚拟内存。理解虚拟内存将会帮助你从全局上更好的理解计算机系统是如何工作的。
- 虚拟内存功能非常强大 虚拟内存使得应用程序可以大块大块的创建以及destroy (毁坏)内存,大块的将磁盘文件映射到主存里面,还有进程之间去共享内存。
- 虚拟内存比如危险(dangerous). 应用程序每一次引用(reference)变量, 解引用(dereference) 一个指针, 或者调用了一次动态内存分配,比如malloc. 如果虚拟内存被不正确的方式使用,就会引起潜在的,莫名其妙的错误,比如, 一个错误的指针就会让程序挂掉,抛出segmentation fault 或者 Protection fault 异常.
这篇文章我们从两个角度去看虚拟内存,第一部分说说虚拟内存是如何工作的,第二部分说说应用程序如何去使用以及管理虚拟内存。 有一个不可避免的事实是虚拟内存确实比较复杂。 好消息是如果你了解了其中细节,你完全可以手动去模拟一下虚拟内存,这样一趟下来,虚拟内存就不再神秘莫测了。
第二部分建立在理解了虚拟内存之后,将会在你面前展示如何在程序中去管理虚拟内存, 你会学到怎么通过显式(explicit)的内存映射,还有类似malloc 的动态内存分配。
物理地址和虚拟地址
计算机系统的主存是由M 个连续的cell(每个cell 大小为一个byte) 组成的阵列(array), 每个byte 都有一个唯一的物理地址(physical address) . 第一个byte 地址为0.接下来的byte 地址为1, 后面依次这样排下去。基于这种组织方式,对于CPU去访问内存来说,最自然的方式就是使用物理地址,我们把这种方法叫物理寻址(physical addressing).
下图展示了一个load 指令去读一个起始地址为4的一个word.
当cpu 执行一次load 指令时,CPU 会生成一个有效的物理地址,然后通过memory bus 传给主存,最后将主存相应位置的内容抓出来,存到CPU 寄存器里面。
早期的PC, 嵌入式,包括超级计算机CRAY 都是用的这种物理寻址(physical addressing). 但是,现代,计算机都是用的虚拟内存的方式, 见下图
使用虚拟地址时,CPU 访问主存首先生成一个虚拟地址,在发送给内存之前,会转换为物理地址,虚拟地址转换为物理地址这个过程叫地址转换(address translation). 就像异常处理一样,地址翻译,需要CPU与操作系统紧密合作,CPU 里面专门做这个事情的叫内存管理单元(memory management unit), 具体过程就是操作系统使用一份位于主存中的look up 表。
地址空间
一个地址空间是一个有序的非负整数地址
{0, 1, 2, ...}
如果在地址空间里面的这些整数是连续的,我们就说,这是一个线性地址空间,为了简化我们讨论的内容,我们始终假设我们用到的地址空间是线性地址,在一个支持虚拟内存的系统里,虚拟地址空间N=2^n.
{0, 1, 2, ..., N - 1}
举个例子, 一个虚拟地址空间N=2^n, 我们说,这是一个n位的地址空间,一个典型的现代系统一般支持32位或者64位的地址空间。
另外,一个计算机系统还有物理地址空间,对应着系统里面的M byte:
{ 0, 1, 2, ..., M -1}
M 并不需要是2的幂(power of 2), 但是为了后续讨论的方便,我们假设M=2^m.
举个例子, 一个虚拟地址空间N=2^n, 我们说,这是一个n位的地址空间,一个典型的现代系统一般支持32位或者64位的地址空间。
另外,一个计算机系统还有物理地址空间,对应着系统里面的M byte:
{ 0, 1, 2, ..., M -1}
M 并不需要是2的幂(power of 2), 但是为了后续讨论的方便,我们假设M=2^m.