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. 华南理工大学 Python第6章课后测验-2

    1.(单选)以下关于语句 a = [1,2,3,(4,5)]的说法中,正确的个数有( )个.(1)a是元组类型   (2)a是列表类型  (3)a有5个元素      (4)a有4个元素(5)a[1] ...

  2. Windows 2012 R2 iSCSI server

     Windows 2012 R2可以充当一台简单的SAN,提供iSCSI方式的连接,供客户端使用.不确定是否有人会这么使用,但至少在做实验的时候我觉得挺方便的.不用再像以前专门安装windows ...

  3. Java 快速开发几 MB 独立 EXE,写图形界面很方便

    Java 写的桌面软件带上运行时只有 6 MB,而且还是独立 EXE 文 件,是不是难以置信? 想一想 Electron 没写多少功能就可能超过百 MB 的体积,Java 写的桌面软件算不算得上小.轻 ...

  4. 第六章:Django 综合篇 - 11:分页 Paginator

    分页功能是几乎所有的网站上都需要提供的功能,当你要展示的条目比较多时,必须进行分页,不但能减小数据库读取数据压力,也有利于用户浏览. Django又很贴心的为我们提供了一个Paginator分页工具, ...

  5. Elastic:Sense chrome - 在 chrome 浏览器中操作 Elasticsearch 的插件

    文章转载自:https://elasticstack.blog.csdn.net/article/details/114533904 Sense 是我们现在所说的 Kibana 控制台 UI 的第一个 ...

  6. 9.使用nexus3配置Python私有仓库

    搭建Python私服,我们依旧使用nexus3. 与其他私服一样的,Python私服同样有三种类型: hosted : 本地存储,便于开发者将个人的一些包上传到私服中 proxy : 提供代理其他仓库 ...

  7. 矩阵顺时针打印(C++)(? 为什么不能AC,9度1391)

    #include <iostream> #include <fstream> using namespace std; int a[1000][1000]; void prin ...

  8. 基于纯前端类Excel表格控件实现在线损益表应用

    财务报表也称对外会计报表,是会计主体对外提供的反映企业或预算单位一定时期资金.利润状况的会计报表,由资产负债表.损益表.现金流量表或财务状况变动表.附表和附注构成.财务报表是财务报告的主要部分,不包括 ...

  9. JavaFx 使用字体图标记录

    原文:JavaFx 使用字体图标记录 - Stars-One的杂货小窝 之前其实也是研究过关于字体图标的使用,还整了个库Tornadofx学习笔记(4)--IconTextFx开源库,整合5000+个 ...

  10. laravel config()获取null

      常规开发获取config值的是否发现获取值为null找了下文档,发现laravel是要把config注册到$app里面.   找到这个目录.加入这一行.就可以了