考虑以下场景。你有一个慢的安装 Linux 的只读介质(例如写保护的拇指驱动器、CD/DVD 等)(本身不是 Live CD,而是普通版本),并在没有其他存储形式的计算机上使用它。它很慢,因为它是 USB 2。根文件系统安装为overlayfs,以便它可以“写入”日志和您所做的许多其他临时工作,但所有写入都会写入RAM(tmpfs upperdir)。对于 Live 发行版来说,这是非常典型的场景。
由于没有其他形式的存储,所以swap挂载在zram上。因此,当 Linux 决定交换时,它会压缩这些页面并将它们仍然存储在 RAM 中,但至少它们被压缩了。这实际上是不错的,因为大多数应用程序的 RAM 都很容易压缩(RAM 通常在数据上非常冗余,因为它意味着“快”)。这适用于应用程序内存,但不适用于 tmpfs。
事情是这样的:zram 是快速地,难以置信。另一方面,拇指驱动器速度很慢。假设它是 20 MiB/s,相比之下确实很慢。您可以在这里看到问题以及为什么内核不会做正确的事情。
注意这个问题是不是的重复如何使 TMPFS 内的文件更有可能交换。问题几乎相同,但我对该问题的答案不满意,抱歉。内核肯定是这样的不是自己做“正确的事”,无论如何聪明的设计它的人是。我不喜欢人们不了解情况并认为自己更了解情况。他们迎合一般情况。这就是 Linux 如此可调整的原因,因为无论它多么聪明,它都无法预测它的用途。
例如,我可以(并且确实)设置虚拟机交换性(/proc/sys/vm/swappiness) 到 100,这告诉它积极交换应用程序内存并保留文件缓存。这个选项很好,但不幸的是它并不是一切。
我希望它优先保留文件缓存处理交换时超过任何其他 RAM 使用。这是因为删除文件缓存会导致它必须从慢速 20 MiB/s 驱动器中读回,这要慢得多。很多比交换到 zram 慢。对于应用程序,vm.swappiness 有效,但不适用于 tmpfs。
tmpfs 作为页缓存挂载,因此与文件缓存具有相同的优先级。如果您从 tmpfs 读取文件,它将优先于较旧的文件缓存条目(最近使用的)。但这很糟糕,内核清楚才不是在这里做正确的事。应该考虑到将 tmpfs 交换为 zram 会比文件缓存“最近使用”要好得多,因为从驱动器读取数据非常慢。
因此,我需要明确告诉它与文件缓存相比更频繁地从 tmpfs 进行交换:它应该比 tmpfs 更保留文件缓存。 /proc/sys/vm 中有很多选项,但我找不到任何相关选项。真是令人失望。
如果做不到这一点,有没有办法告诉内核某些设备/驱动器比其他设备/驱动器慢得多,并且它应该更愿意为它们保留缓存而不是其他设备/驱动器? tmpfs 和 zram 速度很快。拇指驱动器不是。我可以告诉内核这个信息吗?
如果它对所有驱动器都一视同仁,它本身就无法做“正确的事情”。将 tmpfs 交换到 zram 等快速驱动器比从慢速驱动器删除缓存要快得多,即使最近使用了 tmpfs。
当它用完可用内存时,它将由于交换性而开始交换应用程序内存(好),或者删除旧文件缓存(坏)。如果我最终重新读取这些文件,速度会非常慢。比决定交换一些 tmpfs(即使最近使用过)然后再次读取要慢得多。因为 zram 快了一个数量级。
答案1
增加交换性值使内核更愿意交换 tmpfs 页面,而不太愿意从不受交换支持的其他文件系统中逐出缓存页面。
由于 zram swap 比拇指驱动器更快,因此理想情况下您希望将 swappiness 提高到 100 以上。这仅在内核版本 5.8 或更高版本中可行。 Linux 5.8 允许将 swappiness 设置为最大 200。
对于内存交换,例如 zram 或 zswap,可以考虑超过 100 的值。例如,如果针对交换设备的随机 IO 平均比来自文件系统的 IO 快 2 倍,则交换性应为 133 (x + 2x = 200, 2x = 133.33)。
进一步阅读
tmpfs 的处理方式与任何其他可交换内存相同
查看内核提交“vmscan:将 LRU 列表拆分为匿名和文件集”——
将 LRU 列表分为两部分,一组用于由真实文件系统(“文件”)支持的页面,一组用于由内存和交换(“匿名”)支持的页面。后者包括 tmpfs。
- 以及代码linux-4.16/mm/vmscan.c:2108-
/*
* Determine how aggressively the anon and file LRU lists should be
* scanned. The relative value of each set of LRU lists is determined
* by looking at the fraction of the pages scanned we did rotate back
* onto the active list instead of evict.
*
* nr[0] = anon inactive pages to scan; nr[1] = anon active pages to scan
* nr[2] = file inactive pages to scan; nr[3] = file active pages to scan
*/
static void get_scan_count(struct lruvec *lruvec, struct mem_cgroup *memcg,
struct scan_control *sc, unsigned long *nr,
unsigned long *lru_pages)
{
int swappiness = mem_cgroup_swappiness(memcg);
...
/*
* With swappiness at 100, anonymous and file have the same priority.
* This scanning priority is essentially the inverse of IO cost.
*/
anon_prio = swappiness;
file_prio = 200 - anon_prio;
Linux 5.8 允许交换性值最大为 200
随着快速随机 IO 设备(SSD、PMEM)和内存中交换设备(例如 zswap)的出现,交换可能比文件系统快得多,并且交换比抖动文件系统缓存更可取。
允许设置 swappiness(定义页面缓存和交换支持页面之间缓存未命中的粗略相对 IO 成本),以通过配置交换首选范围来反映此类情况。
这是 Linux 5.8 中一系列补丁的一部分。在以前的版本中,Linux“主要采用页面缓存并推迟交换,直到虚拟机面临严重的内存压力”。这是因为“算法发展过程中旋转驱动器的寻道成本很高,这也意味着错误可能会因过于激进的交换(主要是随机 IO)而很快导致锁定。”
本系列旨在解决这个问题。自从提交(“a528910e12ec mm:基于抖动检测的文件缓存大小调整”)以来,我们对错误 IO 进行了精确跟踪 - 回收错误页面的最终成本。这使我们能够使用基于 IO 成本的平衡模型,该模型在缓存抖动时更积极地扫描匿名内存,同时能够避免不必要的交换风暴。
这些补丁将 LRU 平衡基于每个列表上的失败率,乘以交换设备和文件系统之间的相对 IO 成本(swappiness),以便优化回收以最小化所产生的 IO 成本。