接下来的研究就是cache了,只要我们加深了对这些概念的理解,还有前面说的MMU。我们在将来研究内存管理的时候就有谱了。在这里我们就先打打基础,所以我在这里记录一下我对cache的认识。
cache(高速缓存) write buffer(写缓存)
对于cache的工作原理在很多网站上都可以搜索得到,在这里我就不讲述这方面的内容了。我想先说说地址映像方式。地址映像方式是指主存的内容是以什么函数或是算法映射到cache上的。地址映像方式大概分为3种:直接映射,全相联映射,组相联映射。不管是哪个算法都好,都会涉及3个方面的问题:第一就是命中率,第二是地址对比的复杂度,第三是不命中的情况下的替换问题。我们接下来大概说说他们吧。
直接映射:每个内存地址指定映射到cache中的一个指定地址的方式。具体来说,假设cache有2N块,规定内存块的大小为2MB,i是内存块的标号,j是cache块的标号。有了这些前提条件后,我们来实际举个例子来说明一下吧。假设我内存是256MB的,这样内存块的标号就是1号~128号。cache分为4块,加上地址映射方式是j=i mod 2N。好了,你就会发现内存块的1号,2号,3号,4号分别对应cache块的0号,1号,2号,3号。如果你继续求余,你会发现这是一个循环来的。好了,这样你就大概知道是哪些内存块被指定到cache的0号区,哪些内存块被指定到1号区......工作原理明白后,我们会发现地址对比的复杂度小,我们可以很有规律找到我们所需内存的块对应于cache中的什么位置。其实,直接映射有个弊端就是如果同一cache块的其它内存块,也想进入cache中,但是之前已经有了别的内存块。这样的操作会引起冲突。这样冲突率高了后就会直接影响命中率了。
全相联映射:主存中的每一个字块可映像到cache任何一个字块位置上。这种方式只有当cache装满的时候才会有冲突的,所以冲突率比较低,可以达到很高的cache命中率。当要访问一个块的数据时,就要求块地址与cache块表中的所有地址标记进行比较,以便确认是否命中了。所以说这种方式的地址比较的复杂度是很高的。
组相联映像:其实它是上面两者的折中,就是把cache分为若干个组。组与组之间是直接映像的方式,组里面的是全相联映像的方式。如果每个组是1的话,这样cache就分为2N个组了,这样就是直接映像。如果组的大小就是cache的大小的话,这样就是全相联映像了。对于它的地址比较复杂度和命中率的问题,很明显是介于上面两者的中间拉。
接下来,我们来讨论cache的写入方式。我们先来讨论一下它的必要性。cache只是一个缓存的作用,换句话来说,如果cache和cpu之间的操作改变了cache中的数据,这样一定要记得把cache更新后的内容准确及时写到内存里面。这样是为了保持内存和cache一致性的问题。想想看,cache的内容会因为写满需要相关数据的时候被替换掉某些内容,如果被替换的内容是之前被跟新过的,又没有写回到内存。这样就会出现很大的问题。对于cache的写入方式,可以分为3种:直写式、缓冲直写式还有回写式。
直写式(write through):就是cpu写入cache的同时也写入内存,这样就可以保持一致了。但是每次cache更新都要访问一次内存,这样速度会变得很慢的。
缓冲直写式(post write):很明显就是在原来直写式的基础上加上一个缓冲器,用它来锁存cpu写入内存的数据,这样cpu就不需等待了。
回写式(write back):通过在cache的每个数据块的标志字段增加一个更新位。若cache的某数据块的数据被更新之后,但是还未被写入内存,这时这个标志位就会置1。每次cpu要写一块新的cache块数据到cache当中时,就会判断这个被替换的cache的更新位是否为1,如果是就先把它写入内存,否则就直接替换。
cache其实会分为ICache和DCache这两个缓存的,前者是指令Cache后者是数据cache。由于对于程序的运行,指令是不需要更新的,而数据则需要更新。所以只是DCache才需要有回写的功能。
现在我来说说cache的工作原理:
首先要把cache和内存划分为同样大小的块,假设有主存B块号和块内地址W,还有cache的b块号和块内地址w。当cpu访问cache时需要地址变换,我们先把B块号和内存地址W存放在主存地址寄存器当中,接着我们通过地址变换部件把B块号和W块内地址变换成cache的b块号和w块内地址。如果变换成功就是命中了。但是不命中的话,就直接使用主存地址寄存器当中的地址来访问主存,读取相应的字,同时把相关的一些字作为块一起读入cache当中,如果cache满了,就要用相应的替换方法去替换一些不常使用的块。
有人肯定会有疑问,这个地址变换部件到底是怎么样工作的?那好,我现在就来大概讲述一下。还记得我之前说的地址映像的概念吗?就是说主存的内容通过某种函数映射到cache里面,也就是为什么我们可以根据传给cpu的是主存地址,而可以访问到其对应在cache里面的单元(前提这个内容在cache里面存在)。我们就是靠这个映射函数的,对于不同的映射函数会有不同的变换方法。在ARM里面,采用的组相联映射方式,大家还记得它的工作方式吧。我现在来说说他的变换方法。
如果cache块的大小为2^L,那么虚拟地址31位~L位都相同的话,就表明他们在同一个块里面。如果cache组的大小是2^S,那么虚拟地址L+S-1位~L位用于表示组里的哪个块。剩下的31位~L+S位表示虚拟地址是属于哪个组的。就这样,我们就可以通过虚拟地址找到这个单元应该映射到cache的哪个组、哪个块还有块内的哪个字(单元)。如果我们发现现在这个单元不在cache里面的话,我们就......其实就是上面的过程。说到这里,大家都该明白应该是怎么映射和变换的。但是对于替换的算法,这个我就不深入去讨论了。
我们现在来具体讨论下s3c2440的cache吧!
首先,2440里面有ICache和DCache,还有写缓存(write buffer)来提高内存访问效率。ICache大小为16KB,一共有512行(entry),每行有8个字。只要对CP15的C1寄存器的12位置1的话,就可以使能ICache了。还记得我们之前在MMU说的描述符的内容吗?其实二级页表的页表项描述符的3位是用来表示C的,2位用来表示B的,其实这两位是为了表示这个这段存储区域(1MB)是否可以用cache的功能,还有是否可以用write buffer的功能。如果这段内存有又cache的功能,我们在cache里面如果得不到我们想要的指令或是数据的话,我们去内存里面拷贝,同时会把读到的内容复制到cache里面。
然后,我们来说说DCache吧。DCache和ICache的结构类似,不同在于每行还有一些其他的内容,他们分别如下:
有两个修改标志位:第一位来表示前4位数据的,后一位来表示后4位数据的。还记得我之前说过了write back类型的cache吗?其实就是一种cache的一种写入方式。当有数据在cache更新了,这样它对应哪行的对应修改标志位会置1。这样就可以根据这个来判断我们是否要把替换掉的数据先写入内存当中。
PA TAG 地址(物理标签地址):这个东西很有用,就是当某些在cache的数据被更新了,同时还要替换掉这个数据的时候,就需要把新的数据写入内存当中。但是写到哪里去呢?就是写到这个物理标签地址的单元里面啦。
如果需要使能DCache的话,就要往CCR置1,CCR是CP15的C1寄存器的第2位。
对于write buffer具体怎么用,我们又表格可以遵循的,但是总得来说,CP15的C1寄存器的第二位(CCR)决定是否开启dcache和write buffer的功能,描述符的c和b位决定这两个东西的使用规则。