如果CPU正在处理相应的内存请求,如何保证内存页不会被驱逐?

问题描述

一般来说,在 cpu 中,内存请求会通过内存层次结构。

然而,当操作系统或其他东西决定从内存中驱逐页面时,

与内存请求相关但仍在 cpu 中处理的页面不得从内存中驱逐。

如何保证这样的页面不被从内存中驱逐?

是否有类似页面引用计数器之类的东西来防止在 cpu 中处理请求时页面被驱逐?

解决方法

从 Linux 的角度来看,有一些机制可以帮助确保可以安全地处理这种情况。

1) Linux 处理页面替换策略的方式。 Linux 使用多个 LRU 列表作为其页面缓存。对于文件支持和匿名页面,每个页面都有各自的活动和非活动页面列表。当一个页面被使用时,CPU 将通过其在相应页表条目 (pte) 中的 PAGE_BIT_ACCESSED flag 将该页面标记为已使用。发生这种情况时,Linux 可以将该页面放入活动列表中。一段时间后,页面替换处理程序尝试将页面踢出,但页面在被踢出之前必须经过几轮和几步。它的访问位必须关闭,然后它必须放在非活动列表中。每次处理程序执行这些步骤之一时,它都会让页面存活并移动到列表的其余部分寻找要驱逐的页面,也许直到它再次到达我们的页面。由于这些步骤中的每一步都涉及对 LRU 列表的多次迭代,因此当处理程序返回到我们的页面以驱逐它时,可能使我们的页面最初处于活动状态的任何 CPU 操作都已经结束。另一方面,如果当我们的页面在等待被驱逐的非活动列表上时 CPU 再次访问该页面,则其访问位将被打开,并且它可以再次被放置在活动列表中。这里的关键是 Linux 通常会尝试在非活动列表中保留一定数量的页面,以便在需要驱逐时只选择非活动页面。这是一种防止出现活动页面被踢出的情况的方法。

2) 交换。假设您的场景确实发生了。您描述驱逐的方式使被驱逐的活动内存看起来像是一个致命错误。实际上,当一个页面被逐出时,要么是一个写回磁盘的文件支持页面,要么是一个写入交换空间的匿名页面(假设你有交换空间)。因此,除非出现任何电源故障,否则在驱逐期间不会丢失内存。此外,保存与此页面对应的数据的任何缓存条目都将无效。因此,当 CPU 尝试读取/写入页面时,它会将缓存条目视为无效,从而引发页面错误。与任何其他页面错误一样,页面将被带回内存并通过缓存/内存层次结构向上。 (页面错误期间 CPU 的 good explanation 行为)

3) 页面锁定。如果您确实需要锁定页面,Linux 有办法做到这一点。在 Linux 内核中,有在 I/O 操作或 page/page_table 操作期间使用的页面锁定函数(如 lock_page())。还有可以从用户空间调用以将特定内存锁定到 RAM 中的 mlock() family of functions。请注意,mlock 不保证内存保持在相同的物理地址,只是保持在 RAM (guaranteeing at worst a soft page fault) 中。