程序跑起来都是跟地址相关的,可以试想,如果多个程序一起跑,难道不相互影响吗?又或者说 为什么我们写代码,从不去关心我们的程序在哪里运行?大家都用一个Memory,为什么没有相互破坏?好神奇!!
为了让多个程序在同一台计算机上更好的运行,而不发生相互破坏性影响,虚拟内存的概念被提了出来,从此多个程序都有了独立的地址空间。
虚拟内存,是与物理内存相对应的,可以设想,我们程序a运行起来,写地址Mx1,程序b运行起来 也写Mx1,结果是,他们没有相互影响,为什么?因为这个地址Mx1是虚拟内存,是假的,程序a的mx1 和程序b的mx1不是同一个地址,程序a写Mx1,结果写到哪去了呢?我们不知道,这件事是操作系统 完成的,他把程序a的Mx1映射到了一个地方,又把程序b的Mx1映射到了另一个地方,这两个程序就能 独立的跑起来了。
解决上述问题的方法很简单,是有一个叫做页表的东西来完成的。
内存分页
为了更好的解决这个问题,我们将内存分页,就成了多个page,假设一个page有16KB,那么page里面的 地址就有16KB=2^14B,个地址,我们需要用14bits来表示这个地址,所以,基于这种page模式 地址的低14位就是page offset,页偏移量,其他的高位叫做page number,在虚拟内存里面 叫做Virutal page number,在物理内存里面叫做Physical page number ,注意物理内存和虚拟内存 的page offset是一样的。于是我们一个page一个page的映射,一次映射就是一整页。页内部保持地址 一致,地址映射只是page number变化。
页表
我们之前说过,操作系统进行了一次映射,这个映射是怎么完成的呢?于是叶表出来了,有一张表 记录了所有虚拟内存地址页号(vpn)到物理内存地址页号的(ppn)的所有信息,显然为了高效性 这里我们使用数组来完成,在c++里面如果有一个数组a[4]={1,2,0,3};那么就有a[0]=1,a[1]=2,a[2]=0, a[3]=3;ok 映射完成了!!O(1)映射,高性能。但是还是有一个问题。这个数组可能回究极大大大大。 设想一个64GB的物理内存,以及1024GB的虚拟内存,页大小16KB,那么会导致36位的 physical address 40位的virtual address 以及14位的page offset,然后我们发现physical page number 达到了22位,virtual page number 达到了26位,我们来算一下这个数组有多大,26位的元素,2^26*22?bit,大概是150MB左右 这么大,缓存也放不下呀,只能放内存里面,可是这这东西读取频率是究极高的,放内存里面读写太慢了。
Translation Look-Aside Buffers (TLBs)
于是我们思考能不能把一部分经常要用到放到Cache里面?能啊!!为什么不能?为了让叶表用的更加得劲, 我们还专门给你弄一个Cache,重新取名位TLB。哈哈。TLB和普通Cache一样,page offset和 TLB index TLB tag和Cache的一摸一样,计算方法一摸一样,此处不再多言。
TLB Entry
这也是一样的,只是多一个Access Control (一般 2 bits),也就多两位来着。Cache懂了这里很简单。