ARM Linux中的cache (2)

关于cache的一些知识

内存管理单元MMU除了使用页表完成物理地址和虚拟地址的映射外,还包括对这些地址的管理。这些管理是通过设置内存属性完成的,比如是否过cache、cache属性、是否可读、可写、可执行、内存类型(normal 、device-GRE)、是否共享,共享范围等等… 这些属性都在页表项中定义,属性的有效范围至少是一个页面。

1)Cache的结构

现在Cache一般组相连方式的,比如sharkl3 L1Dcache是4路128组相连。简单说,就是先把cache分成相等的4块,每块叫做一个way;每个way中有若干行cache line(缓存行),缓存行的编号就是set(行索引)。

内存中的地址到cache的映射是按照一定的规则确定的,下面是这个规则的定义:

对于32bit的地址,分为三部分: Tag-----Set-----offset,offset(字节索引)和缓存行的大小相关,比如64Bytes的缓存行,[5:0]是offset;set(行索引)和cache的中每个way的缓存行的数目有关,32KB的cache除以4way,每个way是8KB,每个way中的缓存行size是64Bytes,那一个way中有128个缓存行,[12:6]是set,剩余的高地址成为Tag(页帧号),作为不同地址的区分标记。

31----------Tag-----------13  12--------Set--------6  5-----offset-----0

如果不同地址中的[12:6]是相同的,它们会映射到相同的set编号上,因为是4ways,所以最多能支持4个set相同的地址映射到cache上,如果多于4个就会涉及到cache的替换策略,一般有循环计数(把最老的替换出去)、FIFO和伪随机策略,我之前见过的cache替换策略都是伪随机的。

MMU支持地址映射,所以cache的地址根据地址是虚拟地址还是物理地址分为VIVT、PIPT、VIPT三种。I就是上文的set。T就是上文的Tag

VIVT:就是用虚拟地址访问cache

PIPT:用物理地址访问cache

VIPT:虚拟地址的set和物理地址的Tag

V的好处是不用经过TLB和MMU,直接使用虚拟地址访问cache,比较方便。但是也有坏处:

● 因为多个虚拟地址可以对用一个物理地址,所以会造成一个物理地址的内容可能在多个缓存行中,即便一个虚拟地址对应的缓存行内容发生变化,其他对应缓存行都变成无效,需要无效化清洗,比较麻烦。所以又出现了P的方式。

● 进程间的虚拟地址是相互独立的,当发生进程切换时,新进程中可能有相同的虚拟地址空间,显然新进程访问这个地址空间如果从cache访问是有问题的,需要把之前进程的虚拟地址对应的cache都无效化。每次切换都需要cache无效化(TLB也有这个问题,linux采用ASID改善)。

● 因为这样cache模块会在mmu模块之前,造成页面的保护机制缺失。

 

目前CA55应该是PIPT的方式,那么cpu访问内存的路径就是下面的样子

Cpu core  <------------------> MMU(包括TLB) <------------->Cache <------------>DDR

P的坏处:需要经过mmu模块,有翻译地址映射的时间成本。

VIPT因为I部分(set行索引)和实际的物理地址部分未必完全一样,需用“着色”技术。

2)Cache的属性

这个属性实际上是某个地址空间上的cache属性。

● 分配策略

Cache对读写操作是分开管理的,所以有读分配和写分配的说法,当然都是发生在cache miss时(不命中)

读分配:为读操作分配缓存行

写分配:为写操作分配缓存行,当前地址会多次被写时分配。

分开管理有利于一些特殊场合的管理,比如读多写少时,仅使用读分配比较合适。一些算法,原始的数据仅仅用于运算,所以对于原始数据的地址只需要读分配就好了。

下面用memcpy介绍一下读分配的过程。每次都数据时,比如每次读4字节数据(不考虑SIMD情况),然后把这4个数据写入写地址上。开了读分配后,在读4字节时发现未命中,cpu会预取多个缓存行(由CPUECTLR. L1PCTL定义,最多8 outstanding,默认5 outstanding),比如预取4行数据(64x4=256Bytes数据,AXI上开启burst传输,最大长度256Bytes),cpu再去读第二个4字节数据时,就不用去ddr读,直接从cache中读就可以了。

● 写策略,目前arm都采用写回策略。

写通:当cache写命中时,cache与主存同时发生写修改,会造成AXI总线时刻繁忙,效率也低。

写回:当CPU对cache写命中时,只修改cache的内容不立即写入主存,只当此行被换出时才写回主存。有脏页的情况,需要刷新操作。

● 共享策略 指明了共享的范围,sharkl3的cache都是inner的

Inner:内部集成,由协处理器方式访问的

Outer:外挂的,控制状态寄存器映射到总线地址空间

        

PS:关于地址空间共享的inner和outer,像自旋锁的arm实现使用的独占访问指令需要开启shareable才能执行成功。独占访问指令由独占监视器监视,inner和outer决定使用那个监视器。

3)cache的操作

●(简化一下,仅考虑一级cache)

 clean:将cache中的数据写入ddr中,一般是cpu更改了cache中的数据

 invalid:将cache中的数据无效化,cpu重新从ddr中读,一般是外设更改了ddr中的数据

 zero:将cache中的部分数据清0,比如为ZI段分配缓存行。

 lock:锁定某些缓存行,不被替换出去

多核多cluster的cpu,它们cache间的一致性是由硬件保证的(如cci、scu等,以及MOESI协议)

 

● POU和POC 数据一致性视角: 比如说clean数据到poc或者pou是什么意思?

POU:统一点(处理器视角的局部性一致),如cpu和它的内部模块看到L2 cache的内容是一样的,但看到L3 cache的内容可能不是一样的,那么L2 cache就是统一点。

POC:一致点(整个系统视角的全局性一致),所有的外设和cpu(注意是所有,不是一部分)对DDR的内容看到是一样的,那么DDR称之为一致点。可见一致点比统一点的要求严格。

 

如果POU是L3 cache且要求clean到POU,那么cpu刷cache最终刷到L3 cache为止,而不需要到ddr。

         Sharkl3的POC和POU是一样的 都是DDR

         有些CMP的POC和POU是不一样的,SMP的似乎没有必要分清这个区别。

         ● 多级cache访问,以L1、L2两级cache为例

           LDR r0, [r1] 从r1的保存地址中读数据,如果这个地址是cacheable的,那么

  1. 数据在L1 cache中,即命中,直接读回就好了
  2. 数据不在L1 cache中,但是在L2 cache中,将L2 cache中的数据读到L1 cache中
  3. 数据只在ddr中,那么需要为L1、L2 cache分配缓存行,并将数据读回。

你可能感兴趣的:(内核学习)