假设一个程序请求一些内存,但没有足够的可用内存。Linux 有几种不同的响应方式。一种反应是选择其他一些最近没有被访问过的使用过的内存,并将这个不活动的内存移动到交换。
但是,我看到许多超出此范围的文章和评论。他们说即使有大量空闲内存,Linux 有时也会决定将非活动内存写入交换。提前写入交换意味着当我们最终要使用这块内存时,我们不必等待磁盘写入。他们说这是优化性能的深思熟虑的策略。
他们是对的吗?或者这是一个神话?引用您的来源。
请使用以下定义来理解这个问题:
- 交换
- free memory - free命令显示的“空闲”内存。这是
MemFree
来自 的值/proc/meminfo
。/proc/meminfo
是内核提供的虚拟文本文件。请参阅proc(5)或RHEL 文档。 - 即使有大量空闲内存——为了论证的目的,假设有超过 10% 的空闲内存。
参考
以下是一些搜索词:linux“机会交换”或(交换“当系统无事可做时”或“当它无事可做时”或“当系统空闲时”或“在空闲时间”)
在 Google 的第二高结果中,StackExchange 用户询问“为什么在 RAM 中有足够多的可用空间时使用交换?”,并复制free
显示大约 20% 可用内存的命令结果。在回答这个具体问题时,我看到这个答案被高度评价:
Linux 在 RAM 被填满之前开始交换。这样做是为了提高性能和响应能力:
性能提高了,因为有时 RAM 更适合用于磁盘缓存而不是存储程序内存。所以最好换掉一段时间不活动的程序,而将常用文件保存在缓存中。
通过在系统空闲时换出页面来提高响应能力,而不是在内存已满并且某些程序正在运行并请求更多 RAM 来完成任务时。
当然,交换确实会减慢系统速度——但交换的替代方法不是不交换,而是拥有更多的 RAM 或使用更少的 RAM。
Google 上的第一个结果已被标记为与上述问题的重复 :-)。MemFree
在这种情况下,提问者从 16GB 中复制了显示 7GB 的详细信息。这个问题有一个自己被接受和赞成的答案:
仅当没有空闲内存时才进行交换,只有设置
swappiness
为 0 时才会发生这种情况。否则,在空闲时间,内核将交换内存。在这样做时,数据不会从内存中删除,而是在交换分区中制作一个副本。这意味着,如果出现内存耗尽的情况,它不必当场写入磁盘。在这种情况下,内核可以只覆盖已经交换的内存页面,因为它知道它有一份数据副本。
该
swappiness
参数基本上只是控制它执行此操作的程度。
另一个引用没有明确声称交换的数据也保留在内存中。但是,如果您在有 20% 可用内存的情况下进行交换,那么您似乎更喜欢这种方法,并且这样做的原因是为了提高性能。
据我所知,Linux 确实支持在主内存和交换空间中保留相同数据的副本。
我还注意到“机会交换”发生在“空闲时间”的普遍说法。我知道它应该有助于让我放心,这个功能通常对性能有好处。我没有在上面的定义中包含这个,因为我认为它已经有足够的细节来提出一个很好的明确问题。我不想让这变得比它需要的更复杂。
原始动机
当我有千兆字节的可用内存时,顶部显示“swout”(交换)。为什么?
有一些这样的报告,当有足够的可用内存时,Linux 会写入交换。“机会性交换”可以解释这些报告。同时,至少提出了一个替代原因。作为查看可能原因的第一步:Linux 是否曾经执行过上述定义的“机会交换”?
在我报告的示例中,问题现已得到解答。原因不是机会主义的交换。
Linux 不执行此问题中定义的“机会交换”。
以下主要参考文献根本没有提到这个概念:
进一步来说:
基于上述,当空闲页面的数量高于“高水位线”时,我们不会期望任何交换。
其次,这告诉我们这样做的目的
kswapd
是为了让更多的空闲页面。当
kswapd
写入一个内存页进行交换时,它会立即释放内存页。 kswapd 不会在内存中保留交换页面的副本。Linux 2.6 使用“ rmap ”来释放页面。在 Linux 2.4 中,情况更加复杂。当一个页面被多个进程共享时,kswapd 无法立即释放它。这是古老的历史。所有链接的帖子都是关于 Linux 2.6 或更高版本的。
此引用描述了一种特殊情况:如果您将
swappiness
值配置为0
. 在这种情况下,我们不应该期待任何交换,直到缓存页面的数量下降到高水位线。换句话说,内核会在开始交换之前尝试丢弃几乎所有的文件缓存。(这可能会导致速度大幅下降。您需要有一些文件缓存!文件缓存用于保存所有正在运行的程序的代码 :-)水印是什么?
上面的引用提出了一个问题:我的系统上的“水印”内存预留有多大?答:在“小型”系统上,默认区域水印可能高达内存的 3%。这是由于“min”水印的计算。在较大的系统上,水印的比例较小,接近内存的 0.3%。
因此,如果问题是关于一个具有超过 10% 可用内存的系统,那么这个水印逻辑的确切细节并不重要。
每个单独“区域”的水印显示
/proc/zoneinfo
在proc(5)中。我的 zoneinfo 的摘录:当前的“水印”是
min
、low
和high
。如果一个程序曾经要求足够的内存来减少free
,min
程序进入“直接回收”。程序在内核释放内存时等待。如果可能,我们希望避免直接回收。因此,如果
free
低于low
水印,内核会唤醒kswapd
。kswapd
通过交换和/或删除缓存来释放内存,直到再次free
高于high
。附加条件:
kswapd
还将运行以保护完整的 lowmem_reserve 数量,以供内核 lowmem 和 DMA 使用。默认的lowmem_reserve约为 RAM 的前 4GiB(DMA32 区域)的 1/256,因此通常在 16MiB 左右。Linux 代码提交
Linux 代码
有时声称更改
swappiness
为0
将有效地禁用“机会交换”。这提供了一个有趣的调查途径。如果有一种叫做“机会性交换”的东西,并且它可以通过交换性进行调整,那么我们可以通过找到所有读取的调用链来追踪它vm_swappiness
。请注意,我们可以通过假设CONFIG_MEMCG
未设置来减少搜索空间(即“内存 cgroup”被禁用)。调用链如下:shrink_node_memcg
被评论为“这是一个基本的每个节点页面更自由。被 kswapd 和直接回收使用”。即这个函数增加了空闲页面的数量。它不会尝试复制要交换的页面,以便可以在以后释放它们。但即使我们打折:上面的链是从三个不同的函数调用的,如下所示。正如预期的那样,我们可以将调用站点分为直接回收和 kswapd。在直接回收中执行“机会交换”是没有意义的。
因此,可能声称 kswapd 以某种方式被唤醒,即使所有内存分配都立即从可用内存中得到满足。我查看了 的用法
wake_up_interruptible(&pgdat->kswapd_wait)
,但没有看到任何这样的唤醒。不,Linux 中不存在机会交换。我花了一些时间研究这个问题,所有来源(教科书、内核开发人员邮件列表上的电子邮件、Linux 源代码和提交评论,以及与 Mel Gorman 的一些 Twitter 交流)都告诉我同样的事情:Linux 只回收内存以响应某种形式的内存压力(明显的休眠除外)。
关于这个主题的所有流行误解可能源于一个简单的事实,即 Linux 不能等到空闲内存的最后一个字节才开始交换。它需要某种缓冲来保护它免受极端形式的内存耗尽,并且有一些可调参数可以影响缓冲的大小(例如
vm.min_free_kbytes
)。但这与“交换,因为没有更好的事情可做”不同。不幸的是,页框回收算法相对于 2.6 变得更加复杂(当它在 Mel Gorman 的书中详细描述时),但基本思想或多或少是相同的:页回收是由失败的分配触发的,然后要么唤醒
kswapd
或尝试同步释放页面(取决于内存压力、分配标志和其他因素)。页面分配可能开始失败并剩余足够的可用内存的最明显原因是它们可能要求连续的内存,而实际上内存可能过于碎片化而无法满足请求。从历史上看,Linux 内核开发人员竭尽全力避免需要连续分配。尽管如此,一些设备驱动程序仍然需要这样做——或者因为它们不能进行多页内存 I/O(分散-聚集 DMA),或者它可能只是驱动程序开发人员的草率编码。透明大页面 (THP) 的出现为在物理上连续的块中分配内存提供了另一个原因。
大约在同一时间范围内引入的区域压缩应该有助于解决内存碎片问题,但它并不总是产生预期的效果。
有各种
vmscan
跟踪点可以帮助您了解在您的特定情况下到底发生了什么——当有特定的调用堆栈时,在 Linux 内核代码中找到您需要的东西总是更容易,而不是仅仅扫描看起来远程相关的所有内容。