Linux 上的 Swappiness 是什么? (以及如何改变它)

关于Linux交换性(Swappiness)的深入理解

关于Linux的交换性(swappiness)值,一个常见的误解是它与内存(RAM)使用量直接相关,并在达到特定阈值时触发交换操作。实际上,这是一种广泛流传但并不准确的说法。本文将深入解释交换性的真正含义。

揭秘交换性的迷思

交换(swap)是一种内存管理技术,它将内存中不常用的数据转移到硬盘上的特定区域(交换分区或交换文件),从而释放内存空间。
随机存取存储器
(RAM) 。

Linux系统中存在一个名为“swappiness”的设置,但其作用常常被误解。最常见的错误观点是,swappiness值设定了RAM使用的阈值,一旦达到这个阈值,系统就会开始进行交换操作。

这种误解如此普遍,以至于现在已成为一种常识。如果几乎每个人都告诉你交换性是这样运作的,那么当说不是这样时,你为什么要相信我们呢?

很简单。我们将证明这一点。

内存区域的划分

Linux并非将所有RAM视为一个同质的内存池,而是将其划分为多个称为“区域(zone)”的不同部分。计算机上存在的区域类型取决于其是
32位
还是
64位
系统。以下是对x86架构计算机
上可能存在的区域的简化描述:

  • 直接内存访问 (DMA): 这是低16MB内存区域。该区域之所以如此命名,是因为在早期,某些计算机只能直接访问这一特定的物理内存区域。
  • 直接内存访问 32 (DMA32):
    虽然名称如此,但 DMA32 区域仅存在于 64 位 Linux 系统中。它是低 4GB 的内存。 在 32 位计算机上运行的 Linux 只能对这个数量的 RAM 进行 DMA(除非他们使用
    物理地址扩展
    (PAE)内核),这就是该区域的名称。 虽然,在 32 位计算机上,它被称为 HighMem。
  • 正常 (Normal): 在64位计算机上,正常内存是4GB以上的所有RAM(大约)。在32位机器上,它是介于16 MB 和 896 MB之间的 RAM。
  • 高内存 (HighMem): 仅存在于 32 位 Linux 计算机上,它包括 896MB 以上的所有内存,甚至在足够大的计算机上,可以包括超过 4GB 的内存。

页面大小

RAM以固定大小的页面(page)进行分配。这个大小由内核在启动时根据计算机的体系结构确定。通常,Linux计算机上的页面大小为4KB。

你可以通过
getconf命令
查看你的页面大小:

getconf PAGESIZE

区域与节点

区域与节点(node)相关联。节点与中央处理器(CPU)关联。内核会尽量从与当前CPU关联的节点中为在该CPU上运行的进程分配内存。

节点绑定到CPU的概念允许在专业的多CPU计算机中安装混合内存类型,这被称为
非统一内存访问(NUMA)
架构。

这通常用于高端的服务器。普通的Linux计算机通常只有一个节点,即“节点零”。所有区域都将属于该节点。可以通过查看/proc/buddyinfo文件来查看计算机中的节点和区域。以下是如何使用less命令:

less /proc/buddyinfo

这是一台64位计算机的输出示例:

Node 0, zone DMA   1  1  1  0  2  1  1  0  1  1  3
Node 0, zone DMA32 2 67 58 19  8  3  3  1  1  1 17

该计算机只有一个节点,即“节点零”。这台计算机只有2GB的内存,所以没有“正常(Normal)”区域。只有两个区域,DMA和DMA32。

每一列代表特定大小的可用页面数量。例如,对于 DMA32 区域,从左侧读取:

  • 2:有 2 个 ^(0*PAGESIZE) 个内存块。
  • 67:有 2^(1*PAGE_SIZE) 个内存块中的 67 个。
  • 58:有 2^(2*PAGESIZE) 个可用内存块中的 58 个。
  • 以此类推,一直到……
  • 17:有 17 个 2^(512*PAGESIZE) 块。

但实际上,我们查看此信息的唯一原因是查看节点和区域之间的关系。

文件页面和匿名页面

内存映射使用
页表条目
记录哪些内存页面被使用,以及用于什么目的。

内存映射可以是:

  • 文件支持(File-backed): 文件支持的映射包含从文件中读取的数据。 它可以是任何类型的文件。 重要的一点是,如果系统释放了此内存并需要再次获取该数据,则可以再次从文件中读取它。 但是,如果内存中的数据已更改,则需要将这些更改写入硬盘驱动器上的文件,然后才能释放内存。 如果这没有发生,更改将丢失。
  • 匿名(Anonymous): 匿名内存是没有文件或设备支持的内存映射。 这些页面可能包含程序即时请求的内存以保存数据,或者用于诸如
    堆栈


    . 由于这类数据背后没有文件,所以必须专门留出一个地方来存放匿名数据。 那个地方就是交换分区或交换文件。 在释放匿名页面之前将匿名数据写入交换。
  • 设备支持(Device-backed): 设备通过块设备文件寻址,这些文件可以被视为文件。 可以从它们读取数据并写入它们。 设备支持的内存映射具有存储在其中的设备的数据。
  • 共享(Shared): 多个
    页表条目
    可以映射到RAM的同一页。通过任何映射访问内存位置将显示相同的数据。通过更改这些共同监视的内存位置中的数据,不同的进程可以以非常有效的方式相互通信。共享可写映射是实现高性能进程间通信的常用方法。
  • 写时复制(Copy-on-write):
    写时复制是一种惰性分配技术。 如果请求已在内存中的资源的副本,则通过返回到原始资源的映射来满足请求。 如果“共享”资源的进程之一试图写入它,则必须在内存中真正复制资源,以允许对新副本进行更改。 所以内存分配只发生在第一个写命令上。

对于交换性(swappiness),我们只需要关注列表中的前两个:文件页面和匿名页面。

交换性的真正含义

这是来自
Linux文档
中关于swappiness的描述:
GitHub:

“该控件用于定义内核交换内存页面的积极性(原文如此)。较高的值会增加积极性,较低的值会减少交换量。值0指示内核在空闲和文件支持的页面数量小于区域中的高水位线之前不启动交换。”

“默认值为60。”

这听起来像是交换强度使交换强度上升或下降。有趣的是,它指出将 swappiness 设置为零并不会关闭交换。它指示内核在满足某些条件之前不要交换。但是交换仍然可能发生。

让我们深入研究一下。这是vm_swappiness的定义和默认值,可以在
内核源代码文件vmscan.c
中找到:

/*
* 从 0 .. 100 开始。更高意味着更易交换。
*/
int vm_swappiness = 60;
  

swappiness 值的范围可以从 0 到 100。同样,评论听起来确实像 swappiness 值与发生多少交换有关,更高的数字会导致更多的交换。

在源代码文件中,我们可以看到一个名为swappiness的新变量被分配了一个值,该值由函数mem_cgroup_swappiness()返回。对源代码进行更多跟踪将显示该函数返回的值是vm_swappiness。所以现在,变量swappiness设置为等于vm_swappiness设置的任何值。

int swappiness = mem_cgroup_swappiness(memcg);


同一个源代码文件中再往下一点,我们看到这个:

/*
* 当swappiness为100时,匿名和文件具有相同的优先级。
* 这个扫描优先级本质上是 IO 开销的倒数。
*/
anon_prio = 交换性;
file_prio = 200 – anon_prio;
  

那很有意思。 两个不同的值来自交换性。 anon_prio 和 file_prio 变量保存这些值。 随着一个增加,另一个减少,反之亦然。

Linux的swappiness值实际上设置了两个值之间的比例。

文件页面与匿名页面的优先级

文件页面保存的数据在内存释放时可以方便地重新获取。Linux可以直接从原始文件中读取这些数据。但是,如果文件在RAM中的数据发生了更改,这些更改必须先写入到文件中才能释放该文件页面。如果数据没有更改,系统可以直接从磁盘读取,无需写入操作。因此,文件页面通常不会被写入到交换分区或交换文件中,而是被“存储”回其原始文件。

对于匿名页面,由于没有相关的原始文件,这些页面中的值是动态生成的,无法简单地从磁盘读取。唯一恢复匿名页面内存值的方法是在释放内存之前将数据存储在某处。这个地方就是交换空间。

无论是文件页面还是匿名页面,释放内存都可能涉及硬盘写入。如果数据自上次写入文件或交换空间以来发生改变,则需要进行文件系统写入。检索数据时,需要读取文件系统。这两种类型的页面回收都有可能很耗费资源。试图通过最小化匿名页面的交换来减少硬盘驱动器的输入和输出只会增加处理写入和读取文件的文件页面所需的硬盘驱动器输入和输出量。

从代码片段可以看出,有两个变量:file_prio(文件优先级)和 anon_prio(匿名优先级)。

  • anon_prio变量被设置为 Linux swappiness 值。
  • file_prio值被设置为 200 减去 anon_prio 值。

这两个变量包含协同工作的值。如果它们都设置为100,则它们是相等的。 对于任何其他值,anon_prio 将从 100 减少到 0,而 file_prio 将从 100 增加到 200。这两个值会输入一个复杂的算法,该算法确定 Linux 内核是否以回收(释放)文件页面或匿名的偏好运行页。

您可以将 file_prio 视为系统释放文件页面的意愿,而将 anon_prio 视为系统释放匿名页面的意愿。这些值不做的是为何时使用交换设置任何类型的触发器或阈值。这是其他地方决定的。

但是,当需要释放内存时,回收和交换算法会考虑这两个变量以及它们之间的比率,以确定优先考虑释放哪些页面类型。这决定了相关的硬盘活动是处理文件页面的文件还是处理匿名页面的交换空间。

交换何时触发?

我们已经确定 Linux 的 swappiness 值设置了对将被扫描以进行潜在回收的内存页面类型的首选项。 这很好,但是必须决定什么时候交换会介入。

每个内存区域都有一个高水位线(high watermark)和一个低水位线(low watermark)。这些是系统派生值,它们是每个区域中 RAM 的百分比。正是这些值用作交换触发阈值。

要检查您的高水位线和低水位线,请使用以下命令查看 /proc/zoneinfo 文件:

less /proc/zoneinfo

每个区域都有一组以页为单位的内存值。以下是测试机器上 DMA32 区域的值。低水位线是 13966 页,高水位线是 16759 页:

在正常运行条件下,当区域中的可用内存降至区域的低水位线以下时,交换算法开始扫描内存页面以寻找它可以回收的内存,同时考虑 anon_prio 和 file_prio 的相对值。

如果 Linux swappiness 值设置为零,则当文件页面和空闲页面的组合值小于高水位线时会发生交换。

所以你可以看到你不能使用 Linux swappiness 值来影响交换在 RAM 使用方面的行为。它只是不能那样工作。

最佳的Swappiness设置

这取决于硬件、工作负载、硬盘类型以及您的计算机是台式机还是服务器。显然,不存在一种适用于所有情况的通用设置。

并且您必须记住,交换不仅用作在内存空间不足时释放 RAM 的机制。交换是运行良好的系统的重要组成部分,没有它,Linux 很难实现健全的内存管理。

更改 Linux 的 swappiness 值是即时的,无需重启。因此,您可以进行小幅调整并监控效果。理想情况下,您会在几天内执行此操作,并在您的计算机上进行不同类型的活动,以尝试找到最接近理想设置的设置。

以下是一些需要考虑的要点:

  • 将 Linux 的 swappiness 值设置为零并试图“禁用交换”只是将与交换相关的硬盘活动转移到与文件相关的硬盘活动。
  • 如果您使用的是旧的机械硬盘,您可以尝试降低Linux的swappiness值,以减少匿名页面的回收和交换分区的磨损。当然,当您降低一个设置时,另一个设置会增加。减少交换分区的磨损可能会增加文件系统的磨损。但是您的计算机可能更倾向于选择一种方法而不是另一种方法。唯一确定的方法是进行实验。
  • 对于像数据库服务器这样的专用服务器,您可以从数据库软件供应商处获得指导。很多时候,这些应用程序都有自己专门设计的文件缓存和内存管理例程,您最好依赖这些例程。软件提供商可能会根据机器规格和工作负载建议 Linux 交换值。
  • 对于拥有较新硬件的普通桌面用户?保持原样即可。

如何设置Linux的Swappiness值

在更改 swappiness 值之前,您需要了解其当前值。如果你想减少一点,问题是比什么少一点? 你可以使用以下命令查找:

cat /proc/sys/vm/swappiness

要配置 swappiness 值,请使用
sysctl 命令

sudo sysctl vm.swappiness=45

新的值会立即生效,无需重启。

实际上,如果重新启动,swappiness 值将返回其默认值 60。当您完成实验并决定要使用的新值时,您可以通过将其添加到
/etc/sysctl.conf 文件中来使其在重新启动后保持不变。您可以使用您喜欢的任何编辑器。使用以下命令使用 nano 编辑器编辑文件:

sudo nano /etc/sysctl.conf

当 nano 打开时,滚动到文件底部并添加这一行。 我们使用 35 作为永久交换值。您应该替换您希望使用的值。

vm.swappiness=35

要保存更改并退出 nano,请按“Ctrl+O”,按“Enter”,然后按“Ctrl+Z”。

内存管理的复杂性

内存管理是一项非常复杂的工作。 这就是为什么对于普通用户来说,最好将它留给内核处理。

很容易认为您使用的 RAM 比实际更多。像 topfree 这样的实用程序可能会给人造成错误的印象。Linux 会将空闲 RAM 用于各种目的,例如磁盘缓存。这人为地增加了“已使用”的内存数值,并降低了“空闲”的内存数值。实际上,用作磁盘缓存的 RAM 被标记为“已使用”和“可用”,因为它随时可以快速回收。

对于非专业人士来说,可能看起来交换不起作用,或者交换值需要更改。

一如既往,细节决定成败。或者,在这种情况下,是内核交换守护进程。