C volatile,以及硬件缓存问题

时间:2016-01-26 05:38:22

标签: c multithreading caching

我在这个网站和其他地方读过类似的答案,但在一些情况下我仍然感到困惑。

我知道标准实际上保证了什么,我理解了关键字的预期用途,我很清楚编译器缓存和L1 / L2 / ect之间的区别。高速缓存;为了好奇,我更了解其他案例。

假设我在C中声明了一个变量volatile。四种情况:

  1. 信号处理程序,单线程(按预期):这是关键字要解决的问题。我的进程从操作系统获得信号回调,并且我正在修改我的进程的正常执行中的一些volatile变量。由于它被声明为volatile,因此正常进程不会将此值存储在CPU寄存器中,并且将始终从内存中进行加载。即使信号处理程序写入volatile变量,由于信号处理程序与正常进程共享相同的地址空间,即使volatile变量先前已在硬件中缓存 (即L1,L2),我们保证主进程将加载正确的,更新的变量。完美,每个人都很开心。
  2. DMA传输,单线程:假设volatile变量映射到正在进行DMA写入的内存区域。和以前一样,编译器不会将volatile变量保存在CPU寄存器中,并且总是从内存中加载;但是,如果该变量存在于硬件缓存中,则加载请求将永远不会到达主内存。如果DMA控制器在我们的背后更新MM,我们永远不会获得最新的价值。在抢占式操作系统中,我们可以通过以下事实得到保存:最终,我们可能会被上下文切换出来,并且下次我们的进程恢复时,缓存会很冷,我们实际上必须从主内存重新加载 - 所以我们将获得正确的功能..最终(我们自己的进程也可能会将缓存线交换掉 - 但同样,我们可能会在此之前浪费宝贵的周期)。是否有标准化的硬件支持或操作系统支持,通过DMA控制器更新主内存时通知硬件高速缓存?或者我们是否必须显式刷新缓存以保证我们不会读取错误值? (这在列出的架构中是否可能?)
  3. 内存映射寄存器,单线程:与#2相同,但volatile变量映射到内存映射寄存器(或显式IO端口)。我认为这是一个比#3更难的问题,因为至少DMA控制器会在CPU完成传输时向CPU发出信号,这为OS或HW提供了做某事的机会。
  4. Mutilthreaded:如果我有一个volatile变量,是否有保证在不同物理内核上运行的多个线程之间的缓存一致性?同样可以肯定的是,编译器仍然会从内存中发出加载指令,但如果该值缓存在一个内核的缓存中,是否有任何保证在其他内核的缓存中必须存在相同的值? (我认为在同一物理内核上的不同逻辑内核上超线程线程完全不是问题,因为它们共享物理高速缓存内存)。我压倒性的直觉说没有,但我想我还是会在这里列出这个案例。
  5. 如果可能,区分x64和ARMv6 / 7/8架构,以及内核与用户域解决方案。

1 个答案:

答案 0 :(得分:1)

对于2和3,没有标准化的方法可行。

通常在进行DMA传输时,会依赖于平台刷新缓存。通常,有很简单的指示(从现在开始,缓存集成在CPU中)。

另一方面,当访问内存映射寄存器时,行为通常取决于写入顺序。例如,假设您有一个UART端口并为其写入字符 - 每次从C写入端口时,您都需要确保实际写入该端口。

虽然它可能会在每次写入之间刷新缓存,但它并不是通常所做的。正常的方式(至少对于ARM)是设置MMU,以便对地址空间的某些区域的写入以非正确的顺序进行。

这种方法也可以用于DMA传输的内存;例如,可以设置专用区域作为DMA缓冲区并设置MMU,以便对该区域的读写操作进行缓存。

另一方面,语言保证所有内存(通过声明变量或使用new分配内存得到的内容)将以某种方式运行。它的多线程或涉及的信号之间应该没有区别。请注意,C90和C99标准没有提到线程(C11确实如此),但它们应该以这种方式工作。实现必须确保以与此一致的方式使用CPU和缓存(因此,如果这样做,操作系统可能无法在不同的核心上调度不同的线程)完成)。因此,您不需要刷新缓存以便在线程之间共享数据,但是您确实需要同步线程,当然还需要使用volatile限定数据。对于信号处理程序也是如此,即使实现恰好将它们安排在不同的核心上也是如此。