0%

CPU中cache和MMU的基本逻辑

处理器角度的存储

从CPU核角度看,数据存在于cache和内存两个地方,其中cache又分为L1/L2/L3等不同级别,
数据的索引用地址描述,对cache可以用虚拟地址或者物理地址做索引,而内存只能用物理
地址做索引。

也就是对于内存的访问,不管是用页表还是用TLB,总是需要先翻译得到物理地址,使用物理
地址做访问。内存的访问逻辑比较简单,我们下面重点看cache的访问。

cache的访问需要快,最好是可以直接使用虚拟地址访问cache,当然先做地址翻译,然后用
物理地址访问cache是一定没有错的。但是,用虚拟地址访问cache就会出现同名和重名的问题,
同名是相同虚拟地址在不同时间被映射到不同物理地址上,重名是同一时间,一个物理地址
有两个虚拟地址映射。

同名问题比较简单,说白了就是虚拟地址和物理地址的映射关系变了,这个问题有两个解决办法:
(1) 可以在变化映射关系的时候把原来虚拟地址对应的cache清理掉;(2) 可以引入新的变量
表明不同的虚拟地址到物理地址的映射所属的主体。具体实现上,第一个解决办法需要引入
cache清理的命令,第二个解决办法就是引入ASID,使用虚拟地址+ASID的cache索引方式。

重名问题比较绕,说白了就是一个数据被cache到了不同的cache line上,同步问题需要处理。
直接的处理方式是消灭问题本身,使用虚拟地址但是一个物理地址上的数据还是只能被cache
到一个cache line上,就没有这个问题了,直接的想法就是使用页内偏移做cache索引,因为
一页内,虚拟地址和物理地址是一样的,这样做带来的问题就是cache的容量做不大,理解
这个问题前需要看下cache的映射模式。

《超标量处理器》中把cache的映射模式总结的很清晰: 直接相连是一个地址上的数据只能放
到cache的一个固定位置,全相连是一个地址上的数据可以放到cache的全部cache line上,
组相连是把cache分成一个个组(set),一个地址上的数据只可以放到cache的一个组里,但是
一个组里有多个cache line,数据可以放到特定组的不同cache line上。

1
2
3
4
5
6
7
8
9
10
11
                          way
+------------+------------+------------+-------------+
set0 | cache line | cache line | cache line | cache line |
+------------+------------+------------+-------------+
+------------+------------+------------+-------------+
set1 | cache line | cache line | cache line | cache line |
+------------+------------+------------+-------------+

+------------+------------+------------+-------------+
setN | cache line | cache line | cache line | cache line |
+------------+------------+------------+-------------+

如上是一个4路(way)组相连的cache,如果一个cache line 64Byte,整个cache是32KB,那么
很容易计算出这片cache有32KB/64B/4=128个set,需要7个bit的宽度去描述,加上6个bit描述
cache line里各个Byte的地址,已经超过了12bit的页内偏移。所以,32KB的cache要至少做成
8路组相连cache,才能使用虚拟地址作为index索引cache。那么我们可以得到一个4KB页面下,
特定cache大小最小路数的表出来:

1
2
3
4
5
  8KB   2 way
16KB 4 way
32KB 8 way
64KB 16 way
128KB 32 way

如果不这样做,也可以硬件搞定重名问题,代价是增加硬件的复杂度。(todo)

cache的替换算法。相同index的地址数远大于一个set里的way的数目,cache替换算法决定
一个set里,谁应该被替换出去为新的访问腾出cache line的位置。

cache的操作命令有invalid和clean,invalid cache是无效化对应地址的cache,clean cache
的语意其实是和dirty cache对应的,clean cache把cache上的数据写回到内存,对于数据
只写到cache还没有写回内存的情况,就需要用clean cache把数据同步回内存。

地址翻译

这里我们总结下地址翻译里,容易忽略的几个基本逻辑。

TLB是页表项的cache,这个定义里包含着这些逻辑:页表项的所有内容必须要TLB中有记录,
否则的话,在TLB命中的场景下,页表项里的属性无法体现出来。页表项里既有只读也有可写
的属性,比如access bit和dirty bit就是可以被硬件动态改变的,硬件对页表项的写操作
应该也作用到TLB上,同时TLB和页表项之间应该有同步逻辑。

这里我们想象下页表项里的access bit和dirty bit的基本逻辑。首先,我们定义在页表项里
有access bit和dirty bit这两个bit,当CPU访问一个page时,对应的access bit会被硬件
配置成1,当CPU写一个page时,对应的dirty bit会被硬件配置成1。access/dirty bit的定义
描述是基于页表项的,所以不管怎么说,满足条件时页表项上的对应bit是一定要更新的。
当TLB里有access/dirty bit相关的信息时,硬件可以优化更新页表项上对应bit的行为,如果
TLB里相关的bit已经是1,硬件就可以不去更新页表项的对应bit,因为TLB和页表项里的内容
是一样的。可以看出以上逻辑成立的基础是TLB和页表项需要保持同步,这个同步可以软件来
做也可以硬件来做,最终会表现为软硬件接口的不同。如果软件来做,那么软件在更新页表
项上的access/dirty bit时,需要flush掉对应的TLB,软硬件接口定义上也要明确指出这个
问题。

页表的操作主体有:通过store/load修改和读取;通过MMU硬件隐式的访问和修改。store/load
会和cache系统交互,把页表保存到cache或内存上,所以MMU对页表的隐式操作也会和cache
系统交互。