内存管理——缓存一致性问题
缓存一致性问题
问题1:多核CPU与cache的缓存一致性问题
多核CPU在访问内存时,每个核都有自己的cache,由于cache的写回机制,部分数据没有及时更新到内存,那么在不同线程访问同一个变量的时候就会出现不一致的情况
比如核心A访问address1,把address1的数据加载到A的cache中,核心B也访问address1并加载到B的cache中。如果核心A修改了这个数据,只会把cache中的数据修改并标记为dirty。在没有写回之前,核心B的cache中的同个数据还没改,当B去访问它时则会与A不一致。
要解决这一问题需要一种机制,来同步两个不同核心里面的缓存数据。要实现的这个机制的话,要保证做到下面这 2 点:
- 某个 CPU 核心里的 Cache 数据更新时,必须要传播到其他核心的 Cache,这个称为写传播(Write Propagation)
- 某个 CPU 核心里对数据的操作顺序,必须在其他核心看起来顺序是一样的,这个称为事务的串行化(Transaction Serialization)
事务串行化,也就是让不同核心看到的其他核心对于代码的执行顺序是一致的。
要实现事务串行化,要做到 2 点:
- CPU 核心对于 Cache 中数据的操作,需要同步给其他 CPU 核心;
- 要引入「锁」的概念,如果两个 CPU 核心里有相同数据的 Cache,那么对于这个 Cache 数据的更新,只有拿到了「锁」,才能进行对应的数据更新。
CPU用于保证缓存一致性,实现写传播和事务串行化,使用了基于总线嗅探的 MESI 协议。
MESI代表4种状态,协议用状态机机制降低了总线带宽压力。
MESI | 状态 |
---|---|
Modified | 已修改 |
Exclusive | 独占 |
Shared | 共享 |
Invalidated | 已失效 |
详情参考:https://xiaolincoding.com/os/1_hardware/cpu_mesi.html#mesi-协议
问题2:DMA与cache的缓存一致性问题
因为DMA是直接内存访问,对内存的访问不需要经过CPU,因此如果DMA修改了内存上的CPU访问过的数据,而CPU的cache中并不知道内存已经修改了,CPU读写内存的时候还是cache上的旧数据,造成缓存不一致问题。
也就是说,DMA和CPU可以异步地操作memory,解决访问冲突有两种方法,一种是禁止DMA目标地址范围内的cache功能。
一致性映射,代表函数:
void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp); //分配禁止C域和B域的内存给DMA
void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);
在 arm 平台上会禁止页表项中的 C (Cacheable) 域以及 B (Bufferable)域。C 代表是否使用高速缓冲存储器, 而 B 代表是否使用写缓冲区。
一般驱动使用多,申请一片uncached mem ,这样无需考虑data 一致性。代码流程:对kernel页管理的页面属性设置成uncache,在缺页异常填TLB时,该属性就会写到TLB的存储属性域中。保证了dma_alloc_coherent映射的地址空间是uncached的。
dma_alloc_coherent首先对分配到的缓冲区进行cache刷新,之后将该缓冲区的页表修改为uncached,以此来保证之后DMA与CPU操作该块数据的一致性。
另一种解决冲突的方法:
- 在DMA拷贝前,进行一次CACHE CLEAN,将cache内容dirty回写,清除cache,保证在DMA传输时间内不会有回写动作
- 在DMA拷贝完成之后,进行一次CACHE FLUSH,保证CPU访问目的地址时cache会重新构建,目的地址的值一定是从DDR上读取最新数据。
就能很大概率避免一致性的问题。
问题3:外设寄存器与cache的缓存一致性问题
CPU访问外设的数据寄存器和状态寄存器时,也是需要将寄存器的值映射到内存上进行读写,外设寄存器的值由硬件修改,也可以由CPU读写,因此外设和CPU对寄存器映射的内存访问也是异步的。CPU访问外设寄存器的时候会建立内存映射,然后将内存缓存到cache中,若此时外设寄存器的值发生了改变,CPU是不会发现这个变化而继续访问cache中的旧数据的。
因此cache或写缓冲的存在会带来外设寄存器的缓存一致性问题。
最好将外设寄存器配置成uncache,并不适用写缓冲,让CPU在每次访问时都去读外设端口寄存器。
通常,有cache的平台都有办法对某一段地址范围禁用Cache,一般是在页表中设置的,可以设定哪些页面允许Cache缓存,哪些页面不允许Cache缓存,MMU不仅要做地址转换和访问权限检查,也要和Cache协同工作。