继其他问题中提出的主题之后,我相信当 Linux 内核尝试释放物理 RAM 时,它会在丢弃磁盘缓存中的页面或将其他页面刷新到交换区之间做出某种决定。 我对此不确定,也许我完全误解了这个机制。
无论如何,以下方面之间存在协同作用:
- 内核保留自己的内部磁盘缓存
- 程序内存映射文件
- “常规”应用程序内存被放置在交换文件中
这三种方法均可用于释放物理 RAM,这三种方法均依赖于将“脏”页写入磁盘,或者仅仅知道这些页面已在磁盘上干净地复制并将其从物理 RAM 分配中丢弃。
通过交换空间,可以为各个交换分区和文件赋予优先级,以便在较慢的设备之前使用较快的设备。我不知道其他非交换设备有任何此类配置。
我的问题是:内核如何决定释放哪些内存?
- 它纯粹基于上次访问吗?
- 内核是否同等地使用以上所有三个?
- 它们(以及各个磁盘)之间是否有优先级
- 可以配置吗?
这个问题是由类似的经历驱动的这个但我希望这个问题集中在底层机制上,而不是解决特定问题。
答案1
释放内存物理页的整个任务的名称是回收,它涵盖了许多任务。回收主要由页面分配驱动,具有不同的紧急程度。在卸载的系统上,可以毫不费力地满足页面分配,并且不会触发任何回收。在中等负载的系统上,页面分配仍然可以立即得到满足,但它们也会导致kswapd
被唤醒以执行后台回收。在无法立即满足页面分配的已加载系统上,回收是同步执行的。
可回收页面是存储可以在其他地方找到或可用的内容的页面。这就是典型的平衡行为发挥作用的地方:内容也在文件中的内存(或应该最终成为文件),v.内容不在文件中的内存(并且需要被换出)。前者存储在页面缓存中,后者则不存储,这就是为什么平衡解释通常谈论页面缓存与交换。
优先选择其中一个的决定是在内核中的一个位置确定的,get_scan_count
,由设置控制struct scan_control
。该函数的用途描述如下:
确定扫描匿名和文件 LRU 列表的积极程度。每组 LRU 列表的相对值是通过查看我们旋转回活动列表而不是逐出的扫描页面的比例来确定的。
也许令人惊讶的是,对于名为 的函数get_...
,它不使用返回值;相反,它填充指针指向的数组unsigned long *nr
,其中四个条目对应于匿名非活动页面(未支持、最近未使用的页面)、匿名活动页面(未支持、最近使用的页面)、文件非活动页面(页面缓存中最近未使用的页面)和文件活动页面(页面缓存中最近使用的页面)。
get_scan_count
首先检索适当的“交换”值,来自mem_cgroup_swappiness
。如果当前内存 cgroup 是已启用的非 root v1 cgroup,则使用其交换设置;不然就是臭名昭著的/proc/sys/vm/swappiness
。两种设置共享相同的目的;他们告诉内核
带回换出的匿名页面与重新加载文件系统页面的相对 IO 成本
但在实际使用该值之前,get_scan_count
请确定应应用的总体策略:
- 如果没有交换或匿名页面无法收回在当前上下文中,它将仅跟踪文件支持的页面;
- 如果内存 cgroup 完全禁用交换,它将仅处理文件支持的页面;
- 如果swappiness没有被禁用(设置为0),并且系统内存即将耗尽,它将平等地处理所有页面;
- 如果系统几乎用完文件页面,它将仅处理匿名页面;
- 如果有足够的非活动页面缓存,它将仅查找文件支持的页面;
- 在所有其他情况下,它会根据相应的 I/O 成本调整给予各个 LRU 的“权重”。
一旦确定了策略,它就会迭代所有可逐出的 LRU(按顺序非活动匿名、活动匿名、非活动文件支持、活动文件支持)以确定应扫描每个 LRU 的页数;我将忽略 v1 cgroup:
- 如果策略是“平等地搜索所有页面”,则所有 LRU 中的所有页面都可能被扫描,最大大小由 的
scan_control
移位priority
因子确定; - 如果策略是“仅跟踪文件支持的页面”或“仅跟踪匿名页面”,则相应 LRU 中的所有页面都是候选页面(再次按 移动
priority
),其他 LRU 中没有页面; - 否则,根据交换性调整值。
实际的页面扫描由shrink_lruvec
,它使用上面确定的扫描长度,并重复缩小LRU 直到达到目标(以各种方式调整目标)。完成此操作后,活动/非活动 LRU 将重新平衡。
回到你的问题:
- 页缓存和内存映射文件受到同等对待;
- 页面回收并不纯粹基于上次访问(我没有解释如何使用 LRU,或者重新平衡如何工作;请阅读梅尔·戈尔曼 (Mel Gorman) 的相应章节了解 Linux 虚拟内存管理器了解详情);
- 内核并没有平等地使用它们;它们根据情况有不同的优先级,并且可以配置通过许多控件(
swappiness
、cgroup、低水印阈值...)。
交换优先级仅决定页面在决定交换出后的去向。 (顺便说一句,swappiness
上面的文档和解释应该清楚地表明,I/O 成本没有足够的粒度来很好地处理混合 ZRAM/磁盘交换设置......)
还有很多需要解释,包括如何scan_control
设置,但我怀疑这已经太长了!如果您想跟踪回收成本,您可以在任务延迟核算(也可以看看struct taskstats
)。