Virtual cache的引入
- TLB只是加速了从虚拟地址到物理地址的转换,可以很快地得到所需要的数据(或指令)在物理内存中的位置,也就是得到了物理地址
- 但是,如果直接从物理内存中取数据(或置指令),显然也是很慢的,因此可以使用在以前章节提到的Cache来缓存物理地址到数据的转换过程。
- 实际上,从虚拟地址转化为物理地址之后,后续的过程就和前文讲述的内容是一样的了。
- 因为这种Cache使用物理地址进行寻址,因此称为物理Cache(Physical Cache),使用 TLB 和物理 Cache 一起进行工作的过程如图 3. 26 所示。
存在的问题
现在需要先访问TLB, 再访问physical cache, 必然会增加流水线的延迟;
解决方式: 用虚拟地址来寻址cache;
此时这个cache称之为virtual cache;
既然使用虚拟Cache,可以直接从虚拟地址得到对应的数据,那么是不是就可以不使用TLB了呢?
- 当然不是这样,因为一旦虚拟Cache发生了缺失,仍旧需要将对应的虚拟地址转换为物理地址,
- 然后再去物理内存中获得对应的数据, 因此还需要TLB来加速从虚拟地址到物理地址的转换过程。
virtual cache带来的问题:
Aliasing 问题
不同的虚拟地址,对应相同的物理地址;
这样就会引入一个问题,虚拟地址不同,在virtual cache中会占用不同的way,但是实际上这些虚拟地址都对应的是同一个物理地址,最终导致:
● 浪费了宝贵的cache空间,造成cache等效容量的减少,降低了整体的性能;
● 当执行store时,会将数据写入虚拟地址(tag)对应的way中,但是,实际上当前cache中,有很多tag都对应的是同一个物理地址,那么其他va来读时,读不到最新的数据;
是不是所有的virtual cache都有alising问题?
这取决于page的大小和cache的大小;
- 假设page的大小为4KB, 则VA/PA的低12bits是完全相同的;
- cache的大小,<=4KB, direct-mapped cache;
- 此时索引cache的地址范围,只能在低12bit范围内;
- 此时即使VA不同,但是他们最终索引到的way是一样的,所以,不会出现不同VA,占用多个cache entry的情况;
- 只有cache size > 4KB时,才会出现aliasing问题;
怎么解决aliasing问题
考虑8KB, 直接相连结构的cache的alising 问题;
当两个虚拟地址映射到同一个物理地址时,两个虚拟地址的第 12 位可能是 0、也可能是 1。
也就是说,此时在虚拟Cache中会有两个不同的地方存储着同一个物理地址的值;
解决方式一:
当一个虚拟地址写Cache时,将Cache中可能出现同义问题的两个位置都进行更新,这就相当于将它们作为一个位置来看待;
- 要实现这样的功能,就需要使用物理地址作为Cache的Tag部分(VIPT),并且能够同时将虚拟Cache中两个可能重名的位置都读取出来,这就要在Cache中使用bank的结构,
- 例如图3.29所示的大小为8KB、直接相连结构的Cache就需要两个bank,使用虚拟地址VA[11:0]进行寻址。
- 在写Cache时,两个bank中对应的位置都需要更新,这样在读取Cache时,也就会从两个bank中得到同一个值了;
- 但是这样的方法相当于将Cache的容量减少了一半,显然无法在实际当中使用。
解决方式二: 分bank处理
同样考虑8KB, 直接相连的cache; 内部分为两个4KB的bank;
- 读取时:
- 两个bank的对应位置,数据和tag都读出来;
- VA经过TLB转换后,得到PA, 将PA和tag进行比较,和谁比得上,读谁的数据;
- (由于物理地址需要经过TLB才可以得到,所以当Cache中两个bank输出的值送到多路选择器的时候,物理地址可能还没有从TLB中得到,这样在一定程度上增加了处理器的周期时间)
- 写入时:其实可以同理,都比较一下,判断是否hit;
这种方式需要增加额外的逻辑,比如bank的增加,选择电路的增加;
Homonyms问题
即相同的VA, 对应不同的PA;
这是因为,不同的进程,会存在很多相同的虚拟地址,这些虚拟地址实际上对应的物理地址是不同的,当进程之间进行切换时,如果还是映射到上一个进程的物理地址,肯定是会有问题;
解决方式:
1. 在进程切换的时候,直接invalid所有的TLB entry;
当进程切换很频繁时,就需要经常将TLB和虚拟Cache的内容清空,这样可能浪费了大量有用的值,降低了处理器的执行效率。
2. 给每一个进程,都赋一个ID, 称之为PID/ASID
使用ASID相当于扩展了虚拟存储器的空间,此时仍然是每个进程可以看到整个的4GB的虚拟存储器空间,而且每个进程的4GB都互相不交叠。
例如,当使用8位的ASID时,那么虚拟存储器就有28=256 个4GB的空间,就相当于此时虚拟存储器的空间为256×4GB=1024GB,也就是说,使用ASID就等于扩大了虚拟存储器的空间。
考虑这样一个问题:如果不同的进程之间,想要共享一些page的时候,如何实现?
- 再增加一个标志位,Global(G);
- 当一个页不只是属于某一个进程,而是被所有的进程共享时,就可以将这个Global位置为1,这样在查找页表的时候,如果发现G位是 1,那么就不需要再理会ASID的值,这样就实现了一个page被所有的进程共享的功能;
增加ASID后带来的问题
增加ASID后,会导致地址的范围增大,从而导致页表的索引bit增多,这会导致页表过大,过大的页表会导致其内部出现碎片,降低页表的利用效率;
为了解决这个问题,可以采用三级页表的方式,增加一个额外的页表,使用ASID进行索引; 使用这样的方式,要从虚拟地址中得到需要的数据,相比于两级页表,需要多一次物理内存的访问,这会造成TLB缺失的处理时间变长,是使用ASID带来的一个负面影响,尤其是 TLB 缺失发生的频率很高时,这种负面影响更为严重。
在使用多级页表的系统中,只有第一级页表才会常驻物理内存中,第一级页表的基地址由处理器当中专用的寄存器指定,例如图 3. 32 中的 PTR 寄存器。
- 支持 ASID 的处理器中还会有一个寄存器来保存当前进程的 ASID值,
- 每次操作系统创建一个进程时,就会给当前的进程分配 ASID值,并将其写到 ASID 寄存器中
- 这个进程中所有的虚拟地址都会在前面被附上这个ASID的值。
- 在TLB中, ASID和VPN一起组成了新的虚拟地址,参与地址的比较,这样就在TLB中对不同的进程进行了区分。