我正在努力理解 RHEL 6 中页面框架回收算法的内部工作原理。
更具体地说,我想了解为什么当可用内存没有低于pages_low(甚至pages_high)时,我们会在vmstat中看到si/so的非零值以及其他交换迹象。
来自 vmstat:
procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
13 4 2476036 1533508 486264 10396996 18 22 9674 2790 59364 114558 7 8 81 4 0
即系统上有 1533508 KB 的可用内存。
来自/proc/zoneinfo
Node 0, zone Normal
…
min 130364
low 162955
high 195546
事实上,我们看到非零换入和换出活动(si>0,so>0),而可用内存(相当于大约 375k 页)远高于低内存阈值和高内存阈值,这似乎与文档和文献中如何描述交换活动。
例如 Mel Gorman 的“Understanding Linux Virtual Memory”:
“过去,kswapd 每 10 秒唤醒一次,但现在只有当区域中的空闲页面达到 page_low 数量时,它才会被物理页面分配器唤醒”
随后,这本书对我们所看到的情况提供了一种可能的解释:
“在极端内存压力下,进程将通过调用balance_classzone()同步完成kswapd的工作,而balance_classzone()又调用try_to_free_pages_zone()”
即,当内存分配请求失败或缓慢时,进程可以自行启动区域平衡。然而,尚不清楚这是否可以解释交换,因为 try_to_free_pages_zone 似乎专注于缩小各种缓存。
另外,在观察交换迹象时,我们经常会看到 kswapd 位于顶部,这似乎也与直接回收理论相矛盾。
我在这里缺少什么吗?
更新我专门检查了交换期间的 ExaWatcher ps 输出,我可以看到 kswapd0 进程在这些时间内处于“R”状态。即这排除了直接回收的情况。
最好的问候,尼古拉
答案1
我能够找到至少一种可能导致在可用内存远高于任何区域水位线的情况下将页面交换出主内存的情况。该场景与区域压缩有关,这是虚拟机碎片整理的算法之一。
该过程背后的基本思想是移动页面以创建大的连续虚拟地址块。 “移动”是指更新页面的 PTE,而不是物理移动它们。
压缩算法从一个区域的两端运行两个扫描仪,相互靠近。一台扫描仪搜索要移动的页面,另一台扫描仪搜索可以移动到的空闲页面,最终它们应该在中间的某个地方相遇。
问题是,在区域压缩期间,可能会找到无法移动但可以回收的页面。当发生这种情况时,算法可能会尝试通过交换来回收它。
这里重要的是区域压缩不是由任何水印触发的。相反,每当高阶分配失败时,就会发生这种情况,即,如果内存碎片足够多,仍然有大量可用内存时,就会发生这种情况。
答案2
我找到了另一个可能更合适的答案。事实证明,现代版本的 Linux 内核除了直接和定期回收之外,还具有慢速路径分配,其中 kswapd 在分配连续内存块失败后被唤醒。
当唤醒时,kswapd 会检查区域水印。然而,事实证明,水印并不是它们曾经是的静态区域级数字。相反,它们特定于分配顺序。
即,在决定是否应重新平衡区域时,kswapd 会考虑触发它的失败分配请求的顺序。因此,如果内存碎片足够多,kswapd 将有工作要做。
重新平衡区域时,kswapd 将在缩小文件缓存和从用户进程中窃取匿名页面之间进行选择(除非完全禁用交换)。所以剩下的问题是——为什么 kswapd 选择后一个选项。我认为答案再次是碎片——我认为回收算法可能有一种方法知道通过缩小文件缓存获得的页面可能在物理上不连续。
更一般地说,不仅最近版本的内核添加了内存碎片整理,而且页框回收和压缩内存之间的界限也有些模糊。
不幸的是,所有经典的 Linux 内核教科书都是基于 2.6 或更早的内核版本编写的,因此它们可能具有很大的误导性。