1 基本概念

在嵌入式软件开发中,经常会碰到说某块内存是cache的,还是non-cache的,它们究竟是什么意思?分别用在什么场景?non-cache和cache的内存区域怎么配置?这篇博文将会围绕这几个问题展开讨论。

Cache,就是一种缓存机制,它位于CPU和DDR之间,为CPU和DDR之间的读写提供一段内存缓冲区。cache一般是SRAM,它采用了和制作CPU相同的半导体工艺,它的价格比DDR要高,但读写速度要比DDR快不少。例如CPU要执行DDR里的指令,可以一次性的读一块区域的指令到cache里,下次就可以直接从cache里获取指令,而不用反复的去访问速度较慢的DDR。又例如,CPU要写一块数据到DDR里,它可以将数据快速地写到cache里,然后手动执行一条刷新cache的指令就可以将这片数据都更新到DDR里,或者干脆就不刷新,待cache到合适的时候,自己再将内容flush到DDR里。总之一句话,cache的存在意义就是拉近CPU和DDR直接的性能差异,提高整个系统性能。

2 哪些情况不能用cache

在大部分时候,cache是个很好的帮手,我们需要它。但也有例外,考虑下面几个场景

  • case 1 CPU读取外设的内存数据,如果外设的数据本身会变,如网卡接收到外部数据,那么CPU如果连续2次读外设的操作相差时间很短,而且访问的是同样的地址,上次的内存数据还存在于cache当中,那么CPU第二次读取的可能还是第一次缓存在cache里数据。
  • case 2 CPU往外设写数据,如向串口控制器的内存空间写数据,如果CPU第1次写的数据还存在于cache当中,第2次又往同样的地址写数据,CPU可能就只更新了一下cache,由cache输出到串口的只有第2次的内容,第1次写的数据就丢失了。
  • case 3 在嵌入式开发环境中,经常需要在PC端使用调试工具来通过直接查看内存的方式以确定某些事件的发生,如果定义一个全局变量来记录中断计数或者task循环次数等,这个变量如果定义为cache的,你会发现有时候系统明明是正常运行的,但是这个全局变量很长时间都不动一下。其实它的累加效果在cache里,因为没有人引用该变量,而长时间不会flush到DDR里
  • case 4 考虑双cpu的运行环境(不是双核)。cpu1和cpu2共享一块ddr,它们都能访问,这块共享内存用于处理器之间的通信。cpu1在写完数据到后立刻给cpu2一个中断信号,通知cpu2去读这块内存,如果用cache的方法,cpu1可能把更新的内容只写到cache里,还没有被换出到ddr里,cpu2就已经跑去读,那么读到的并不是期望的数据。该过程如图所示:

另外,做过较为小型的对性能要求不太高的嵌入式裸板程序(例如bootloader,板级外设功能验证之类)的朋友都知道,cache经常会帮倒忙,所以他们往往会直接把CPU的cache功能关掉,损失一点性能以确保CPU写入和读出的数据与操作的外设完全一致。

这里我们可以稍微总结一下:对于单CPU,不操作外设,只对DDR的读写,可以放心的使用cache。对于其它情况,有两种办法:

  • 1 手动更新cache,这需要对外设的机制较为了解,且要找到合适的时机刷新(将cache里的数据flush到内存里)或无效(Invalidate,将cache里的内容清掉,下次再读取的时候需要去DDR里读最新的内容)
  • 2 将内存设置为non-cache的,更准确的说是non-cacheable的

3 怎么设置内存为non-cacheable?

不同的处理器平台对于non-cacheable的处理办法也是不一样的,在高级CPU里,一般在运行中,动态地采用页表的方式来标记某些内存是否是non-cacheable的,例如Linux内核里的 有个常用的函数叫ioremap,在访问外设的时候经常会用到,它的作用是映射外设的物理地址到虚拟地址空间给内核驱动程序使用,在映射时,会将寄存器地址页表配置为non-cacheable的,数据直接从外设的地址空间读写,保持了数据的一致性。

在较为低级的CPU(如ARM Cortex-A5,MIPS R3000.接近于MCU)里,一般是采用编译链接时预设的方法来区分cache区域和non-cache区域,在链接脚本或者scatter file里定义不同的section,将需要设为non-cacheable的内存块放置到特定的section里。CPU在启动时会读取这些配置文件,在启动代码里对不同的section配置MMU或MPU,对non-cache的section映射到CPU认识的特拟地址。此后,CPU再访问内存的时候,经过MMU或者MPU,就会知道这块内存是cacheable还是non-cacheable的了

在MIPS里,程序地址空间被分为了kseg0,kseg1等区域,其中kseg0是0x800000000x9FFFFFFF,它是非映射的、cached;kesg1是0xA00000000xBFFFFFFF,它是非映射的,uncached,他们指向的物理地址空间是相同的,也就是说,0x82001234和0xA2001234指向的物理地址是相同的,但是MIPS对它们的访问方式不同,当取指后CPU得知要访问的是kseg0的空间,会想去cache寻找目标内存,若找不到,才会去物理地址寻找,而如果要访问的地址空间是kseg1内,则CPU会绕过cache,直接去物理地址里进行读和写。

4 non-cacheable区域的大小有限制吗?

答案是否定的,只要你乐意,甚至可以把几乎整个DDR都设置为non-cacheable的,但是这样做付出的代价也是巨大的,你等于放弃了CPU厂家精心设计的cache机制,在每次读写数据都要去访问比CPU速度慢得多的DDR,整个系统速度会被严重拖慢。楼主做过一个测试,在同等条件下,写cache的内存速度大约比non-cache的内存快一倍的!

5 应用案例

假设某个嵌入式应用需要将一个4K的内存拷贝到另一个地址去,源和目的映射的物理地址都在DDR里。很自然的,我们会想到勤劳的搬运工——DMA,让DMA去做这个枯燥而费时的拷贝工作,让CPU忙别的去。经常使用DMA的同学都知道,它有标准的3个步骤:

  1. 对源地址进行cache flush,防止源地址里的内容已经在cache里更新,而DDR里还是过时的内容,DMA可不知道何为Cache
  2. 对目标地址进行cache invalidate,因为这个目标地址里的内容马上就要被覆盖了,cache里如果有它的内容,就是过时的了
  3. 设置源、目的地址和数据长度,启动DMA

    是不是挺麻烦的? 如果源地址和目的地址都是以全局数据的形势预设置好的话,完全可以将它们定义在non-cacheable的段内,这样第1)步和第2)步就都不再需要了。请注意,源和目的地址都需要定义到non-cache的区域,少一个的话,你都很可能得不到正确的结果,因为cache的强随机性,DMA搬完后,你再访问内存里的内容很可能是个很奇怪的数字。

转发自Cacheable VS Non-Cacheable

6 cache flush & cache invalidate

含义:Flush does write back the contents of cache to main memory, and invalidate does mark cache lines as invalid so that future reads go to main memory.

invalidate处理悖论:如果需要invalidate的cache/cache line里面包含无用的数据,需要丢弃;又包含其他进程需要的有用的数据,需要写回内存。

Cacheable VS Non-Cacheable的更多相关文章

  1. Spring缓存注解@Cacheable

    @Cacheable @Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 @Cacheable 作用和配置方法 参数 解释 example value 缓存的名称, ...

  2. Spring-Cache 注解 @Cacheable,@CachePut , @CacheEvict

    1.自动生成key @Bean public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Ob ...

  3. 详解Spring缓存注解@Cacheable,@CachePut , @CacheEvict使用

    https://blog.csdn.net/u012240455/article/details/80844361 注释介绍 @Cacheable @Cacheable 的作用 主要针对方法配置,能够 ...

  4. Spring Boot缓存注解@Cacheable、@CacheEvict、@CachePut使用

    从3.1开始,Spring引入了对Cache的支持.其使用方法和原理都类似于Spring对事务管理的支持.Spring Cache是作用在方法上的,其核心思想是这样的:当我们在调用一个缓存方法时会把该 ...

  5. SpringBoot2.0 @Cacheable 添加超时策略

    SpringBoot2.0 @Cacheable 添加超时策略 逻辑比较简单,废话不多说,直接进入正题: 需求:SpringBoot 利用注解使缓存支持过期时间 (同@Cacheable @Cache ...

  6. Cacheable key collision with DefaultKeyGenerator

    The default is to use the hashcode of each parameter and create another (32-bit) hash code. Obviousl ...

  7. Spring中@Cacheable的用法

    在Spring中通过获取MemCachedClient来实现与memcached服务器进行数据读取的方式.不过,在实际开发中,我们往往是通过Spring的@Cacheable来实现数据的缓存的,所以, ...

  8. Spring 4 Ehcache Configuration Example with @Cacheable Annotation

    http://www.concretepage.com/spring-4/spring-4-ehcache-configuration-example-with-cacheable-annotatio ...

  9. mybatis存取blob对象+@Cacheable实现数据缓存

    参考文档: http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 需求场景: 当前业务通过第三方接口查询一个业务数据, ...

  10. Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)

    Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效) 前言 今天在使用Spring cache的Cacheable注解的过程中遇见了一个Cacheable注解失效的问题, ...

随机推荐

  1. Windows服务器无法配置IP

    前天在给一台服务器配置IP地址的时候发现一个奇怪的问题.IP地址配置之后不生效,还是使用的169.254这个微软保留自动分配地址.由于这个是一台虚拟机,尝试了删除添加网卡也没有用.配置IP不成功的时候 ...

  2. 在工作组的环境中配置Windows 2012 R2的远程桌面服务

    在工作组的环境中配置Windows 2012 R2的远程桌面服务 How to configure Remote Desktop Service in Windows 2012 R2 workgrou ...

  3. ProxySQL 读写分离方法

    转载自:https://www.jianshu.com/p/597b840bf70c (使用正则表达式实现基本的读/写分离) 在这一部分,我将通过一个示例来演示如何通过正则表达式来实现读/写分离. 首 ...

  4. kvm里的虚拟机硬盘和网卡使用virtio驱动

    1.首先从虚拟机的xml文件中找到已经使用virtio驱动的硬件,复制里面的address这行参数出来 <address type='pci' domain='0x0000' bus='0x00 ...

  5. 第五章:Admin管理后台 - 1:自定制Admin

    如果只是在admin中简单的展示及管理模型,那么在admin.py模块中使用admin.site.register将模型注册一下就好了: from django.contrib import admi ...

  6. CentOS 7.7系统安装Redis 6.0.3

    前提操作 避免出现如下的错误 yum -y install gcc tcl yum -y install centos-release-scl yum -y install devtoolset-9- ...

  7. 12. Fluentd部署:多Workers进程模式

    介绍如何使用Fluentd的多worker模式处理高访问量的日志事件.此模式会运行多个worker进程以最大利用多核CPU. 原理 默认情况下,一个Fluentd实例会运行一个监控进程和一个工作进程. ...

  8. C语言指针笔记01

    int num = 90; 定义一个整型变量num int* ptr = &num; 定义一个整型指针变量ptr,指针变量ptr的类型取决于他所需要指向的变量,如这里,ptr要指向int类型变 ...

  9. 企业MES系统与ERP信息集成要素有哪些?

    关于要讲明企业MES系统与ERP信息集成要素有哪些,得先弄清楚他们之间的关系:从工厂的管理来说,ERP在上MES在下,ERP统领企业全局包括MES,为管理层服务,重心在于企业决策,ERP对企业宏观管理 ...

  10. java.lang.ClassNotFoundException: Cannot find class: “com.mysql.jdbc.Driver“的报错问题

    @Testpublic void testConnectionTest5() throws Exception { //1.读取配置文件,获取4个基本信息 InputStream is = Conne ...