阅读这篇文章需要计算机组成原理的基础. 在这里外链上一篇文章《关于读书的流水账》,有读书感受。虚拟存储器的核心思想很独到,在看了前一篇文章中提到的书中才领略到了它的魅力,硬件的东西比较多,但是也绝对可以提高你的软件功力,而且毫无疑问。这篇文章算是读书笔记。
PS:发现文中有很多的英文简写,为了方便阅读在这里总结一下。
------------------------------------------------------------------------
中文 | 英文,英文简写
------------------------------------------------------------------------
物理地址 | Physical Address,PA
------------------------------------------------------------------------
物理寻址 | Physical Addressing
------------------------------------------------------------------------
虚拟地址 | Virtual Addressing,VA
------------------------------------------------------------------------
虚拟存储器 | Virtual Memory
------------------------------------------------------------------------
物理页 | Physical Page,PP
------------------------------------------------------------------------
虚拟页 | Virtual Page,VP
------------------------------------------------------------------------
随机访问存储器 | Random Access Memory //物理存储器,内存
------------------------------------------------------------------------
页 | Page Table,PT
------------------------------------------------------------------------
页表条目 | Page Table Entry,PTE
------------------------------------------------------------------------
虚拟地址 | Virtual Addressing,VA
------------------------------------------------------------------------
把主存看成是由连续字节单元组成的大数组,并且用物理地址(PA)来标识每个数组的单元。CPU需要加载存储器中一个字都时候,就指定这个字的物理地址的首地址,从而将存储器中的数据返回给CPU,通过物理地址来访问存储器的方式就是物理寻址。所以很直观,物理寻址方便很多,然而对于系统来说,直接物理寻址对存储器的管理很不合理。
所以有了虚拟地址(VA),通过虚拟地址访问存储器的方式就叫做虚拟寻址,其实这种说法不是很恰当,因为虚拟地址最终还是会被翻译(这个翻译由硬件和系统协同完成的)成物理地址,从而访问存储器。另外,虚拟地址和物理地址是一一对应的,页就是全相联的。
可以看到,最终访问内存的还是物理地址(PA)。
虚拟存储器,是由N个字节组成的,它存在于磁盘上(注意,是磁盘上,不是存储器内存上)。另外物理存储器和虚拟存储器都是用页来作为磁盘和内存的传输单元。但是CPU是从内存(或者是cache)上取数据啊,而虚拟存储器是存在在磁盘上,因此虚拟存储器必须清楚它的哪些页是已分配(里面的数据是有效的)的,哪些是缓存在内存中(将数据从磁盘中拷贝到CPU能较高速访问物理存储器)的,哪些是已分配但是未被缓存到内存中的(数据是有效的且暂时只存在磁盘中,当需要的时候再缓冲)。
虚拟存储器如何知道它每个页的分配情况呢?页表(PT)从而诞生了,每一个虚拟存储器都有它自己独有的页表,页表为虚拟地址和物理地址的全相联提供了可能。
因为有页表的存在,所以没有必要把虚拟存储器(再声明,是存在在磁盘上的)的所以页都缓存在内存当中,即便CPU访问该虚拟存储器的页不存在在内存当中,那么系统会通过查表,把需要的页从磁盘当中拷贝到内存当中,这里就涉及了页面调度的复杂算法了,而且这种算法不是单一的,这里只简单做个介绍。
页命中的情况,如果虚拟页已经缓存到了物理存储器那么好办,直接从物理存储器中直接读取数据就好了。
还是这张图,来看看简单的命中的情况。假如CPU给出的虚拟地址,地址翻译硬件进行查表,通过虚拟地址定位到了PTE 2(PTE就是页表的条目,上面有注释)的位置,发现它的有效位是1,证明里面的数据是有效的且数据已经缓存到了DRAM当中。所以取出PTE 2中的物理存地址来访问DRAM,可以看到它指向了DRAM中的VP 0,目标查找成功,OVER!!
但是页不命中(缺页)的情况,那就有点麻烦了。
还是这张图,只是修改了一下,CPU给出的虚拟地址指向了尚未缓冲的地址。地址翻译硬件发现PTE 5中数据是有效的,但它并没有从磁盘中缓存到DRAM中。地址翻译硬件发现某个CPU需要的虚拟页没有缓存到DRAM中,就会触发一个缺页的异常,那么对应的缺页异常处理程序就会启动。首先会选择已缓存到DRAM的一个牺牲页,把它拷贝会虚拟存储器的某个位置,再将命中的虚拟页缓存到DRAM当中。
这时候,异常处理过程返回,重新返回到导致缺页异常的指令,重新执行,目标命中,OVER!!另外,经常缺页会降低程序执行的效率,不命中的处罚还是存在的。
时间局部性就是程序的数据能够被重复或者多次引用或者被CPU读取。空间局部性就是CPU要执行的指令或者引用的数据在一段存储空间内。因此局部性能够在一定的程度上降低不命中的可能性。
在这里不得不扯上进程这东西。操作系统为每个进程都分配了一个页表,也就是说每个一个进程都有个虚拟存储器,独立的虚拟地址空间。
所以可以看到,每个进程都有一张页表,所以同一个进程的存储空间在虚拟存储器中可以是不连续的,因为虚拟存储器和页表映射加上缺页缓存的机制让进程“拥有”一个“连续”的空间提供了可能,可实际上,是很有可能是不连续的。CPU提取数据的时候,都会先给出的是虚拟地址,无论页命中还是不命中,最后由地址翻译硬件翻译出来的物理地址都不会有错。
在刚开始学c语言的时候经常用到函数scanf和printf等等一些函数,他们在自己进程中有一个虚拟地址与之对应,这样看起来好像每个进程内scanf和printf是分家的,实际上,由于虚拟地址的映射,每个进程中的这些库函数地址都映射到了物理地址的同一个位置,这就节省了很多的空间,从而又不影响进程的独立性。不得不自己亲手画一张在深入理解计算机系统一书中的一个叫做“进程的虚拟地址空间”图,非常经典。
对于32位的系统,进程的虚拟地址空间有4G(其中包括了内核的和进程本身的),那难道4G都要一一映射到虚拟存储器中?当然不是,进程虚拟空间上有空隙的,也就是说,页表中只记录了有效的虚拟页。在这里看到了我经常从自己嘴里蹦出来的堆栈云云,看了这里我对堆栈有了更近一步的了解,疑云顿消。
一本好书真的可以胜过老师在课堂上讲,而且毫无疑问。
收获都在上面了。
本文完 Thursday, April 26, 2012