首页 > 解决方案 > 缓存一致性(物理标记的缓存的特殊情况)

问题描述

想象一下,您有一个已经完成的进程(现在不在内存中),但是当它运行时,它使用了 0x12345000 物理地址(4KB 页)。现在 MMU 将 0x12345000(物理)分配给另一个刚刚启动的进程。但是,也许您在缓存(物理标记)中有 0x12345 标记和前一个过程的数据。这是一个连贯性问题。它是如何解决的?

编辑:假设是:一个进程完成,另一个进程从磁盘传送到内存到内存的同一页运行。我的问题是:如何防止出现问题?我了解到,在将第二个进程带入内存之前,页面已归零。所以现在在缓存中我们有对应于该页面的零。但是页面有第二个进程的数据。这就是我所理解的,但可能是错误的。

彼得科德斯的答案是完美的!

标签: cachingoperating-systemx86-64cpu-cachedma

解决方案


但是缓存中剩余的数据来自上一个进程

是的,这就是应该发生的事情。缓存只是跟踪物理内存中的内容。这是它唯一的工作。它不知道进程。

如果操作系统不希望新进程看到该数据,内核需要运行一些指令将新数据存储到该页面,覆盖缓存和内存内容。

缓存对这个操作是透明的;数据在缓存中是否仍然很热,或者在内核重用该物理页面时旧进程的数据是否已写回 RAM 都无关紧要。

(有关更多详细信息,另请参阅问题下的评论)。

我知道操作系统将物理页面归零,但这是在主内存中,但我说的是缓存内存中的残留数据。

我认为是您困惑的根源:这种归零发生在 CPU 执行的普通存储指令中。 操作系统在 CPU 上运行,并通过循环存储零的字节(或字)将页面归零。这些存储是普通的可缓存存储,与进入缓存/内存层次结构顶部的任何其他写入相同。

如果操作系统想要将归零卸载到缓存不连贯的 DMA 引擎或 blitter 芯片,那么是的,操作系统必须首先使该页面中的任何缓存行无效以避免您正在谈论的问题,失去连贯性与内存。但这不是正常情况。


顺便说一句,“普通商店”仍然可以很快。例如,现代 x86 CPU 可以使用 SIMD 指令在每个时钟周期存储 32 或 64 字节,或者rep stosb基本上是可以在内部使用宽存储的微编码 memset。AMD 甚至有一条clzero指令将一个完整的高速缓存行归零。但这些仍然是 CPU 指令,其内存视图通过缓存。


为新进程加载新代码/数据

现代 x86-64 系统具有缓存一致的 DMA,因此这不是问题。当内存控制器内置在 CPU 中时,这在现代 x86-64 中很容易,因此 PCIe 流量可以在过去的路上检查 L3 缓存。前一个进程的缓存中哪些缓存行仍然是热的并不重要;进入该页面的 DMA 将这些行从缓存中逐出。(或者使用非 DMA“编程 IO”,数据实际上是由运行在 CPU 内核上的驱动程序代码加载到寄存器中,并通过正常存储存储到内存中,这也是高速缓存一致的)。

https://en.wikipedia.org/wiki/Direct_memory_access#Cache_coherency
一些 Xeon 系统甚至可以 DMA进入L3 高速缓存,从而避免主存延迟/带宽瓶颈(例如用于千兆位网络)并节省电力。 https://en.wikipedia.org/wiki/Direct_memory_access#DDIO

当 DRAM 中的数据发生变化时,没有缓存一致性的旧系统必须小心避免过时的缓存命中。 这是一个真正的问题,它不仅限于开始一个新的过程。为不同文件的新文件重用刚刚释放的(munmapped )页面mmap必须担心它。任何磁盘 I/O 都必须担心这一点,包括写入磁盘:您需要将缓存中的数据同步到 DRAM,然后再将其 DMA写入磁盘。

这可能需要在页面上循环并运行类似的指令clflush,或其他 ISA 上的等效指令。(我不知道操作系统在早于clflush.

这篇LWN 文章:DMA, small buffers, and cache incoherence from 2002 可能是相关的。那时,x86 已经被说成具有缓存一致性 DMA,所以也许 x86 一直都有这个。在 SSE 之前,我不知道 x86 如何可靠地使缓存无效,除非wbinv它非常慢且在系统范围内(使所有缓存行无效,而不仅仅是一页),由于性能原因而不能真正使用。


无论哪种方式(一致与否),操作系统都不会浪费时间将零存储到它将要从磁盘读取的页面。 清零是针对新进程的 BSS 以及它分配的任何页面mmap(MAP_ANONYMOUS),而不是针对其代码/数据部分。

此外,您作为新进程执行的可执行文件可能已经在 RAM 中,在这种情况下,您只需设置新进程的页表。


推荐阅读