页表
操作系统通过将虚拟内存分割为大小固定的块来作为硬盘和内存之间的传输单位,这个块被称为虚拟页(Virtual Page, VP),每个虚拟页的大小为P=2^p
字节。物理内存也会按照这种方法分割为物理页(Physical Page, PP),大小也为P
字节。
页表就是一个存放在物理内存的数据结构,它记录虚拟页与物理页的映射关系。
页表是一个页表条目的集合,每个虚拟页在页表中有一个固定偏移量位置的PTE。PTE仅含有一个有效标记的页表结构,该有效位代表这个虚拟页是否被缓存在物理内存中。
在进行动态内存分配时,例如malloc()
函数或者其他高级语言中的new
关键字,操作系统会在硬盘中创建或申请一段虚拟内存空间,并更新到页表(分配一个PTE,使该PTE指向硬盘上这个新创建的虚拟页)。
由于CPU每次进行地址翻译的时候都需要经过PTE,所以如果想控制内存系统的访问,可以在PTE上添加一些额外的许可位(例如读写权限、内核权限等),这样只要有指令违反了这些许可条件,CPU就会触发一个一般保护故障,将控制权传递给内核中的异常处理程序。一般这种异常被称为“段错误(Segmentation Fault)”。
页命中
虚拟页被分配了物理内存。
页缺失
虚拟页没有被缓存到物理内存中(缓存未命中)被称为缺页。
当CPU遇见缺页时会触发一个缺页异常,缺页异常将控制权转向操作系统内核,然后调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,如果牺牲页已被修改过,内核会先将它复制回硬盘(采用写回机制而不是直写也是为了尽量减少对硬盘的访问次数),然后再把该虚拟页覆盖到牺牲页的位置,并且更新PTE。当缺页异常处理程序返回时,它会重新启动导致缺页的指令,由于现在已经成功处理了缺页异常,所以最终结果是页命中,并得到物理地址。
硬盘和内存之间传送页的行为被称为页面调度。当缺页异常发生,才将页面换入物理内存的策略称为按需调度。
局限性原则:
-
空间局部性原则:一个被访问过的内存地址以及其周边的内存地址都会有很大几率被再次访问。
-
时间局部性原则:一个被访问过的内存地址在之后会有很大概率被再次访问。
如果一个程序没有良好的局部性,将会使工作集的大小不断膨胀,直至超过物理内存的大小,这时程序会产生一种叫做抖动(thrashing)的状态,页面会不断地换入换出,如此多次的读写硬盘开销,性能自然会十分“恐怖”。