Linux swappiness 值与交换开始之前使用了多少 RAM 无关。 这是一个被广泛报道和普遍认为的错误。 我们解释它到底是什么。
目录
打破关于 Swapiness 的神话
交换是一种技术,其中数据在 随机存取存储器 (RAM) 被写入硬盘上的一个特殊位置——交换分区或交换文件——以释放 RAM。
Linux 有一个称为 swappiness 值的设置。 关于此设置控制的内容有很多混淆。 对 swappiness 最常见的错误描述是它为 RAM 使用设置了一个阈值,当使用的 RAM 量达到该阈值时,交换开始。
这是一个经常被重复的误解,以至于现在它被认为是智慧。 如果(几乎)其他人都告诉你这正是swappiness的运作方式,那么当我们说它不是时你为什么要相信我们呢?
简单的。 我们将证明这一点。
您的 RAM 分为多个区域
Linux 并不认为您的 RAM 是一个大的同质内存池。 它认为它被划分为许多不同的区域,称为区域。 您的计算机上存在哪些区域取决于它是否 32 位 或者 64 位. 以下是对可能区域的简化描述 x86架构计算机.
直接内存访问 (DMA):这是低 16 MB 内存。 该区域之所以得名,是因为很久以前,有些计算机只能直接访问该物理内存区域。
直接内存访问 32:尽管名称如此,但直接内存访问 32 (DMA32) 是仅在 64 位 Linux 中发现的区域。 这是低 4 GB 的内存。 在 32 位计算机上运行的 Linux 只能对这个数量的 RAM 进行 DMA(除非他们使用 物理地址扩展 (PAE)内核),这就是该区域的名称。 虽然,在 32 位计算机上,它被称为 HighMem。
正常:在 64 位计算机上,正常内存是 4GB 以上的所有 RAM(大约)。 在 32 位机器上,它是介于 16 MB 和 896 MB 之间的 RAM。
HighMem:这只存在于 32 位 Linux 计算机上。 它是 896 MB 以上的所有 RAM,包括在足够大的机器上超过 4 GB 的 RAM。
页大小值
RAM 以固定大小的页面分配。 该大小由内核在启动时通过检测计算机的体系结构来确定。 通常,Linux 计算机上的页面大小为 4 KB。
你可以看到你的页面大小 使用 getconf 命令:
getconf PAGESIZE
区域附加到节点
区域附加到节点。 节点与中央处理单元 (CPU) 相关联。 内核将尝试从与该 CPU 关联的节点为在该 CPU 上运行的进程分配内存。
节点绑定到 CPU 的概念允许在专业的多 CPU 计算机中安装混合内存类型,使用 非统一内存访问 建筑学。
这都是非常高端的。 普通的 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
有一个节点,节点零。 这台计算机只有 2 GB 的 RAM,因此没有“正常”区域。 只有两个区域,DMA 和 DMA32。
每列代表一定大小的可用页面数。 例如,对于 DMA32 区域,从左侧读取:
2:有 2 个 ^(0*PAGESIZE) 个内存块。
67:有 2^(1*PAGE_SIZE) 个内存块中的 67 个。
58:有 2^(2*PAGESIZE) 个可用内存块中的 58 个。
以此类推,一直到……
17:有 17 个 2^(512*PAGESIZE) 块。
但实际上,我们查看此信息的唯一原因是查看节点和区域之间的关系。
文件页面和匿名页面
内存映射使用 页表条目 记录使用了哪些内存页面,以及用于什么目的。
内存映射可以是:
文件支持:文件支持的映射包含从文件中读取的数据。 它可以是任何类型的文件。 需要注意的重要一点是,如果系统释放了此内存并需要再次获取该数据,则可以再次从文件中读取它。 但是,如果内存中的数据已更改,则需要将这些更改写入硬盘驱动器上的文件,然后才能释放内存。 如果这没有发生,更改将丢失。
匿名:匿名内存是没有文件或设备支持的内存映射。 这些页面可能包含程序即时请求的内存以保存数据,或者用于诸如 堆 和 堆. 因为这类数据背后没有文件,所以必须专门留出一个地方来存放匿名数据。 那个地方就是交换分区或交换文件。 在释放匿名页面之前将匿名数据写入交换。
设备支持:设备通过块设备文件寻址,这些文件可以被视为文件。 可以从它们读取数据并写入它们。 设备支持的内存映射具有存储在其中的设备的数据。
共享:多个 页表条目 可以映射到 RAM 的同一页。 通过任何映射访问内存位置将显示相同的数据。 通过更改这些共同监视的内存位置中的数据,不同的进程可以以非常有效的方式相互通信。 共享可写映射是实现高性能进程间通信的常用方法。
写时复制:写时复制是一种惰性分配技术。 如果请求已在内存中的资源的副本,则通过返回到原始资源的映射来满足请求。 如果“共享”资源的进程之一试图写入它,则必须在内存中真正复制资源,以允许对新副本进行更改。 所以内存分配只发生在第一个写命令上。
对于交换性,我们只需要关注列表中的前两个:文件页面和匿名页面。
交换性
这是swappiness的描述 来自 Linux 文档 在 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 中已更改,则必须先将这些更改写入文件,然后才能释放文件页面。 但是,无论哪种方式,都可以通过从文件中读取数据来重新填充 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 值设置了对将被扫描以进行潜在回收的内存页面类型的首选项。 这很好,但是必须决定什么时候交换会介入。
每个内存区域都有一个高水位线和一个低水位线。 这些是系统派生值。 它们是每个区域中 RAM 的百分比。 正是这些值用作交换触发阈值。
要检查您的高水位线和低水位线,请使用以下命令查看 /proc/zoneinfo 文件:
less /proc/zoneinfo
每个区域都有一组以页为单位的内存值。 以下是测试机器上 DMA32 区域的值。 低水位线是 13966 页,高水位线是 16759 页:
在正常运行条件下,当区域中的可用内存降至区域的低水位线以下时,交换算法开始扫描内存页面以寻找它可以回收的内存,同时考虑 anon_prio 和 file_prio 的相对值。
如果 Linux swappiness 值设置为零,则当文件页面和空闲页面的组合值小于高水位线时会发生交换。
所以你可以看到你不能使用 Linux swappiness 值来影响交换在 RAM 使用方面的行为。 它只是不能那样工作。
Swapiness应该设置为什么?
这取决于硬件、工作负载、硬盘类型以及您的计算机是台式机还是服务器。 显然,这不会是一种适合所有类型的设置。
而且您必须记住,交换不仅用作在内存空间不足时释放 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 比实际更多。 像 top 和 free 这样的实用程序可能会给人错误的印象。 Linux 会将空闲 RAM 用于各种用途,例如磁盘缓存。 这人为地提高了“已用”内存数字并降低了“空闲”内存数字。 实际上,用作磁盘缓存的 RAM 被标记为“已使用”和“可用”,因为它可以随时回收,而且速度非常快。
对于外行来说,可能看起来交换不起作用,或者交换值需要更改。
一如既往,魔鬼在细节中。 或者,在这种情况下,是守护进程。 内核交换守护进程。