Cacheable VS Non-Cacheable
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个步骤:
- 对源地址进行cache flush,防止源地址里的内容已经在cache里更新,而DDR里还是过时的内容,DMA可不知道何为Cache
- 对目标地址进行cache invalidate,因为这个目标地址里的内容马上就要被覆盖了,cache里如果有它的内容,就是过时的了
- 设置源、目的地址和数据长度,启动DMA
是不是挺麻烦的? 如果源地址和目的地址都是以全局数据的形势预设置好的话,完全可以将它们定义在non-cacheable的段内,这样第1)步和第2)步就都不再需要了。请注意,源和目的地址都需要定义到non-cache的区域,少一个的话,你都很可能得不到正确的结果,因为cache的强随机性,DMA搬完后,你再访问内存里的内容很可能是个很奇怪的数字。
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的更多相关文章
- Spring缓存注解@Cacheable
@Cacheable @Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 @Cacheable 作用和配置方法 参数 解释 example value 缓存的名称, ...
- Spring-Cache 注解 @Cacheable,@CachePut , @CacheEvict
1.自动生成key @Bean public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Ob ...
- 详解Spring缓存注解@Cacheable,@CachePut , @CacheEvict使用
https://blog.csdn.net/u012240455/article/details/80844361 注释介绍 @Cacheable @Cacheable 的作用 主要针对方法配置,能够 ...
- Spring Boot缓存注解@Cacheable、@CacheEvict、@CachePut使用
从3.1开始,Spring引入了对Cache的支持.其使用方法和原理都类似于Spring对事务管理的支持.Spring Cache是作用在方法上的,其核心思想是这样的:当我们在调用一个缓存方法时会把该 ...
- SpringBoot2.0 @Cacheable 添加超时策略
SpringBoot2.0 @Cacheable 添加超时策略 逻辑比较简单,废话不多说,直接进入正题: 需求:SpringBoot 利用注解使缓存支持过期时间 (同@Cacheable @Cache ...
- Cacheable key collision with DefaultKeyGenerator
The default is to use the hashcode of each parameter and create another (32-bit) hash code. Obviousl ...
- Spring中@Cacheable的用法
在Spring中通过获取MemCachedClient来实现与memcached服务器进行数据读取的方式.不过,在实际开发中,我们往往是通过Spring的@Cacheable来实现数据的缓存的,所以, ...
- Spring 4 Ehcache Configuration Example with @Cacheable Annotation
http://www.concretepage.com/spring-4/spring-4-ehcache-configuration-example-with-cacheable-annotatio ...
- mybatis存取blob对象+@Cacheable实现数据缓存
参考文档: http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 需求场景: 当前业务通过第三方接口查询一个业务数据, ...
- Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)
Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效) 前言 今天在使用Spring cache的Cacheable注解的过程中遇见了一个Cacheable注解失效的问题, ...
随机推荐
- Dubbo本地调试
dubbo 启动标志 Dubbo service server started <dubbo:reference id="transferTimingUploadHisRPCServi ...
- [Python]-字典-实践经验总结
字典是Python中常用的一个数据类型. 与列表有相似的用法,表现在列表的下标和字典的键值可以通过相似的方式读取数据: list_name[0] = value dict_name['key'] = ...
- Windows Powershell安装错误
今天需要更新一下VMware的 powercli.使用命令install-module -Name VMware.PowerCLI -AllowClobber但是遇到一个错误. Unable to r ...
- Traefik SRE 之使用 Prometheus 进行监控报警
当我们使用 Traefik 作为 Kubernetes 的 Ingress 控制器的时候,我们自然也非常有必要对其进行监控.本文我们将探讨如何使用 Prometheus 和 Grafana 从 Tra ...
- vue-router query和params 传参和接收参数
1.params 方式传递和接收参数 //传参 this.$router.push({ name: 'checkDetailInfo', params:{ fkdNum:fkdNum, jyayStr ...
- localStorage概要
在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localSt ...
- 分享一个Vue实现图片水平瀑布流的插件
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.需求来源 今天碰到了一个需求,需要在页面里,用水平瀑布流的方式,将一些图片进行加载,这让我突然想起我很久以前写的一篇文章<JS两 ...
- struts.xml 中用OGNL表达式取不到中文文件名的原因
在struts2中xml配置如下,以execl文件为例: <result name="success" type="stream"> < ...
- SpringMVC入手项目注解版
SpringMVC入手项目注解版 1.创建Maven项目在pom.xml文件引入相关的依赖 <dependencies> <dependency> <groupId> ...
- 20220729 - DP训练 #2
20220729 - DP训练 #2 时间记录 \(8:00-8:10\) 浏览题面 \(8:10-8:50\) T1 看题想到了建树,从每一个点遍历,若能遍历每一个点,则可以获胜 快速写完之后,发现 ...