cache一致性问题

这周碰到的一个问题,在解决的过程中得到不少高手的热心帮助,把一些总结贴出来,欢迎大家指点。

写一个屏驱动的时候,需要把一块 内核 中用kmalloc分配的 内存 映射到 应用 层中使用。这是一个很simple的需求,很轻松的就可以通过mmap搞定:

驱动部分代码:
1698         if((mmap_addr = kmalloc(PAGE_SIZE, GFP_KERNEL)) == NULL){
1699                 ret = -ENOMEM;
1700                 goto out;
1701         }
1702         SetPageReserved(virt_to_page((mmap_addr)));

....

899         long length = vma->vm_end - vma->vm_start;
900 
901         if (length > PAGE_SIZE)
902                 return -EIO;
903 
904         if ((ret = remap_pfn_range(vma,vma->vm_start,virt_to_phys((void *)mmap_addr) >> PAGE_SHIFT,length,vma->vm_page_prot)) < 0) {
905                 return ret;

....


应用层通过
401         if( ( addr = mmap(NULL, 4096, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0 )) == (void *)-1)
402         {
403                 perror("mmap error\n");
404                 return -1;
405         }
获得地址, 然后往内写入

这是一种通过普通内存设为保留,当成io内存映射并map出去的一种方式。经过测试,发现大部分时候数据是对的,但偶尔数据会出错。随后尝试不采用remap_pfn_range建立页表,而是通过缺页时返回分配页的动态方式来处理,结果仍然一样。

我们猜测是由于cache的原因导致的,于是在mmap的时候使其nocache,仍然无法解决。在实在没有办法的情况下,我们尝试采用在内核读取共享内存时执行flush_all_cache,果然,解决了问题,证明了确实是cache导致的。

那么为什么一开始我们种种尝试未能成功呢?因为我们弄反了方向。我们以为是应用层在读cache,内核读内存,实际上,由于采用remap_pfn_range,或者我们在mmap的时候指定了nocache的方式,应用层读取这片vma的时候是根据其指定的nocache属性去读内存,而内核访问kmalloc的时候却是读内存。如何让内核也读内存呢,很简单,通过ioremap_nocache把kmalloc得到的地址再做一次映射,然后给内核用就可以了。

这就是传说中的Cache Coherence ---缓存一致性。由于cache和内存在某些时候的不一致而导致的不同地址空间分别读写导致的问题。并不是所有体系结构都存在这种问题,比如x86.我曾经在x86上用过mmap并且使用良好,因为x86的体系结构确保了缓存一致性:其总线监听 技术 使当某片被cache的内存被其他请求操作时,会被立刻回写,确保cache与内存的一致性。但这种监听技术会带来性能上的损耗,所以arm是由 软件 来确保这个一致性的:一些时候,比如进程切换,必须通过flush整个cache获得正确的内存访问。

在这里例子里,我们是通过内核和应用都nocache的方式来进行内存共享的。那么双方能不能通过cache的方式来访问呢?我们必须知道,有两种cache:物理cache和逻辑cache,对于armv6以下的arm芯片,采用的是逻辑cache的方式,即cache在mmu之前,而armv6及以上的cpu,cache在mmu之后,cpu送出的要访问的地址先通过mmu进行虚拟/物理的转换,再送到cache。这意味着什么呢?意味着对于我们的cpu(v5),当应用层用其地址空间的地址把共享区从内存加到cache后,内核同样访问这片区域的时候,也可以按照其kmalloc分配的地址将同一片内存中的数据加载到cache的另一行中,这就意味着一个内存块会有2个cache拷贝,也就是传说中的别名。简单地说,如果你用arm11,由于使用物理cache,所以不会有问题,而arm9的话,就很麻烦了。

那为什么有时候访问正确,有时候访问错误呢?这就和cache的替换策略有关了。不像x86由于总线监视的原因,可以在相关内存被touch的时候回写,arm的cache只有当是dirty,并且被cache轮转策略选中需要换出的时候,才会被回写。所以有时候,某些加载共享内存的cache块没有被替换,而相应的内存块又被内核加载到cache形成别名,错误就自然产生了。

这就是cache导致的问题。如果双方采用一样的cache策略,自然cache就是透明的,但是如果是不一样的方式,那么可能就会有问题。具体会有哪些问题,如前所述,就和cpu,体系结构有着密切的关系了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Cache一致性问题是指多个处理器或者处理器核心共享同一块内存区域时,由于缓存的存在,可能会导致数据不一致的问题。当一个处理器修改了内存中的某个数据块时,其他处理器缓存中的该数据块就会变得过期,但是其他处理器并不知道这个变化,这就导致了数据不一致的问题。 解决Cache一致性问题的方法有很多,其中最常用的方法是使用缓存一致性协议。常见的缓存一致性协议有MSI、MESI、MOESI等。这些协议通过在缓存之间进行通信,保证了缓存中的数据的一致性。 下面是一个使用MESI协议解决Cache一致性问题的例子: ```python # 使用Python模拟MESI协议 class Cache: def __init__(self): self.data = None self.state = 'I' # 初始状态为无效状态 def read(self): if self.state == 'I': # 从主存中读取数据 self.data = main_memory.read() self.state = 'S' # 修改状态为共享状态 return self.data def write(self, data): if self.state == 'I': # 从主存中读取数据 self.data = main_memory.read() self.state = 'M' # 修改状态为修改状态 self.data = data def flush(self): if self.state == 'M': # 将数据写回主存 main_memory.write(self.data) self.state = 'I' # 修改状态为无效状态 class MainMemory: def __init__(self): self.data = None def read(self): return self.data def write(self, data): self.data = data # 初始化缓存和主存 cache1 = Cache() cache2 = Cache() main_memory = MainMemory() # 从cache1中读取数据 data1 = cache1.read() # 从cache2中读取数据 data2 = cache2.read() # 修改cache1中的数据 cache1.write('new data') # 从cache2中再次读取数据 data2 = cache2.read() # 将cache1中的数据写回主存 cache1.flush() # 从cache2中再次读取数据 data2 = cache2.read() ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值