C语言 C 易失性变量和缓存内存
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7872175/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C volatile variables and Cache Memory
提问by Microkernel
Cache is controlled by cache hardware transparently to processor, so if we use volatile variables in C program, how is it guaranteed that my program reads data each time from the actual memory address specified but not cache.
缓存由缓存硬件对处理器透明控制,所以如果我们在C程序中使用易失性变量,如何保证我的程序每次从指定的实际内存地址读取数据而不是缓存。
My understanding is that,
我的理解是,
Volatile keyword tells compiler that the variable references shouldn't be optimized and should be read as programmed in the code.
Cache is controlled by cache hardware transparently, hence when processor issues an address, it doesn't know whether the data is coming from cache or the memory.
Volatile 关键字告诉编译器变量引用不应该被优化,而应该按照代码中的编程来读取。
缓存由缓存硬件透明地控制,因此当处理器发出地址时,它不知道数据来自缓存还是内存。
So, if I have a requirement of having to read a memory address every time required, how can I make sure that its not referred from cache but from required address?
因此,如果我需要每次都必须读取内存地址,我如何确保它不是从缓存中引用而是从所需地址中引用?
Some how, these two concepts are not fitting together well. Please clarify how its done.
不知怎的,这两个概念并不能很好地结合在一起。请说明它是如何完成的。
(Imagining we have write-back policy in cache (if required for analyzing the problem))
(想象我们在缓存中有回写策略(如果需要分析问题))
Thank you, Microkernel :)
谢谢你,微内核:)
回答by Andrew Cottrell
Firmware developer here. This is a standard problem in embedded programming, and one that trips up many (even very experienced) developers.
固件开发者在这里。这是嵌入式编程中的一个标准问题,并且会困扰许多(甚至是非常有经验的)开发人员。
My assumption is that you are attempting to access a hardware register, and that register value can change over time (be it interrupt status, timer, GPIO indications, etc.).
我的假设是您正在尝试访问硬件寄存器,并且该寄存器值会随着时间的推移而改变(无论是中断状态、定时器、GPIO 指示等)。
The volatilekeyword is only part of the solution, and in many cases may not be necessary. This causes the variable to be re-read from memoryeach time it is used (as opposed to being optimized out by the compiler or stored in a processor register across multiple uses), but whether the "memory"being read is an actual hardware register versus a cached location is unknown to your code and unaffected by the volatilekeyword. If your function only reads the register once then you can probably leave off volatile, but as a general rule I will suggest that most hardware registers should be defined as volatile.
该volatile关键字是唯一的解决方案的一部分,并且在许多情况下可能不是必需的。这会导致变量在每次使用时从内存中重新读取(而不是被编译器优化掉或在多次使用中存储在处理器寄存器中),但是被读取的“内存”是否是实际的硬件寄存器与缓存位置对您的代码而言是未知的,并且不受volatile关键字的影响。如果您的函数只读取寄存器一次,那么您可能可以不用volatile,但作为一般规则,我建议大多数硬件寄存器应定义为volatile.
The bigger issue is caching and cache coherency. The easiest approach here is to make sure your register is in uncached address space. That means every time you access the register you are guaranteed to read/write the actual hardware register and not cache memory. A more complex but potentially better performing approach is to use cached address space and have your code manually force cache updates for specific situations like this. For both approaches, how this is accomplished is architecture-dependent and beyond the scope of the question. It could involve MTRRs (for x86), MMU, page table modifications, etc.
更大的问题是缓存和缓存一致性。这里最简单的方法是确保您的寄存器位于未缓存的地址空间中。这意味着每次访问寄存器时,您都可以保证读/写实际的硬件寄存器而不是缓存。一种更复杂但可能性能更好的方法是使用缓存地址空间,并让您的代码手动强制缓存更新以针对此类特定情况。对于这两种方法,如何实现取决于体系结构,超出了问题的范围。它可能涉及 MTRR(对于 x86)、MMU、页表修改等。
Hope that helps. If I've missed something, let me know and I'll expand my answer.
希望有帮助。如果我错过了什么,请告诉我,我会扩大我的答案。
回答by user541686
My suggestion is to mark the page as non-cached by the virtual memory manager.
In Windows, this is done through setting PAGE_NOCACHEwhen calling VirtualProtect.
我的建议是将页面标记为未被虚拟内存管理器缓存。
在 Windows 中,这是通过PAGE_NOCACHE调用VirtualProtect.
For a somewhat different purpose, the SSE 2 instructionshave the _mm_stream_xyzinstructions to prevent cache pollution, although I don't think they apply to your case here.
出于某种不同的目的,SSE 2 指令具有_mm_stream_xyz防止缓存污染的说明,尽管我认为它们不适用于您的情况。
In either case, there is no portableway of doing what you want in C; you have to use OS functionality.
在任何一种情况下,在 C 中都没有可移植的方式来做你想做的事情;您必须使用操作系统功能。
回答by Cratylus
From your question there is a misconception on your part.Volatilekeyword is not related to the cache as you describe.
从你的问题来看,你有一个误解。Volatile关键字与您描述的缓存无关。
When the keyword volatileis specified for a variable, it gives a hint to the compiler not to do certain optimizations as this variable can change from other parts of the program unexpectedly.
当volatile为变量指定关键字时,它会提示编译器不要进行某些优化,因为该变量可能会从程序的其他部分意外更改。
What is meant here, is that the compiler should not reuse the value already loaded in a register, but access the memory again as the value in register is not guaranteed to be the same as the value stored in memory.
这里的意思是,编译器不应该重用已经加载到寄存器中的值,而是再次访问内存,因为不能保证寄存器中的值与存储在内存中的值相同。
The rest concerning the cache memory is not directly related to the programmer.
其余有关高速缓存的内容与程序员没有直接关系。
I mean the synchronization of any cache memory of CPU with the RAM is an entirely different subject.
我的意思是 CPU 的任何缓存与 RAM 的同步是一个完全不同的主题。
回答by Zan Lynx
Wikipedia has a pretty good article about MTRR (Memory Type Range Registers)which apply to the x86 family of CPUs.
维基百科有一篇关于适用于 x86 系列 CPU 的MTRR(内存类型范围寄存器)的好文章。
To summarize it, starting with the Pentium Pro Intel (and AMD copied) had these MTR registers which could set uncached, write-through, write-combining, write-protect or write-back attributes on ranges of memory.
总而言之,从 Pentium Pro 开始,Intel(和 AMD 复制)具有这些 MTR 寄存器,可以在内存范围内设置未缓存、直写、写组合、写保护或回写属性。
Starting with the Pentium III but as far as I know, only really useful with the 64-bit processors, they honor the MTRRs but they can be overridden by the Page Attribute Tables which let the CPU set a memory type for each page of memory.
从 Pentium III 开始,但据我所知,只对 64 位处理器真正有用,它们遵守 MTRR,但它们可以被页面属性表覆盖,让 CPU 为每个内存页面设置内存类型。
A major use of the MTRRs that I know of is graphics RAM. It is much more efficient to mark it as write-combining. This lets the cache store up the writes and it relaxes all of the memory write ordering rules to allow very high-speed burst writes to a graphics card.
据我所知,MTRR 的一个主要用途是图形 RAM。将其标记为写组合更有效。这让缓存可以存储写入并放宽所有内存写入排序规则,以允许对图形卡进行非常高速的突发写入。
But for your purposes you would want either a MTRR or a PAT setting of either uncached or write-through.
但出于您的目的,您可能需要 MTRR 或未缓存或直写的 PAT 设置。
回答by Jens Gustedt
As you say cache is transparent to the programmer. The system guarantees that you always see the value that was last written to if you access an object through its address. The "only" thing that you may incur if an obsolete value is in your cache is a runtime penalty.
正如您所说,缓存对程序员是透明的。如果您通过对象的地址访问对象,系统保证您始终看到最后写入的值。如果缓存中存在过时的值,您可能会招致的“唯一”事情是运行时惩罚。
回答by mouviciel
volatilemakes sure that data is read everytime it is needed without bothering with any cache between CPU and memory. But if you need to read actual data from memory and not cached data, you have two options:
volatile确保每次需要时都读取数据,而无需担心 CPU 和内存之间的任何缓存。但是如果你需要从内存中读取实际数据而不是缓存数据,你有两个选择:
- Make a board where said data is not cached. This may already be the case if you address some I/O device,
- Use specific CPU instructions that bypass the cache. This is used when you need to scrub memory for activating possible SEU errors.
- 制作一个不缓存所述数据的电路板。如果您寻址某些 I/O 设备,情况可能已经如此,
- 使用绕过缓存的特定 CPU 指令。当您需要清理内存以激活可能的 SEU 错误时使用。
The details of second option depend on OS and/or CPU.
第二个选项的详细信息取决于操作系统和/或 CPU。
回答by James Zhu
using the _Uncached keyword may help in embedded OS , like MQX
使用 _Uncached 关键字可能有助于嵌入式操作系统,如 MQX
#define MEM_READ(addr) (*((volatile _Uncached unsigned int *)(addr)))
#define MEM_WRITE(addr,data) (*((volatile _Uncached unsigned int *)(addr)) = data)

