LWN:正式建立内存策略zone!
共 3971字,需浏览 8分钟
·
2024-04-11 12:39
关注了就能看到更多这么棒的文章哦~
Formalizing policy zones for memory
By Jonathan Corbet
March 5, 2024
Kimi Chat (https://kimi.moonshot.cn/) translation
https://lwn.net/Articles/964239/
内核的内存管理子系统是建立在“区域”(zones)概念之上的,这些区域最初被添加是为了描述其中包含的内存页的物理特性。随着时间的推移,区域也承担了更多的策略相关角色。通过一套名为 THP 分配器优化(链接 1)的补丁集,Yu Zhao 开始更好地定义 policy 相关区域的角色,目的是为了增加两个新的区域,终极目标是改善内核对透明巨页(Transparent Huge Pages,THPs)的支持。
历史上的 zone
一些背景知识可能有助于了解这一系列补丁的上下文。
最早的运行 Linux 的 x86 系统通过 ISA 总线与外围设备通信,该总线只能使用 24 位地址寻址 16MB 内存。即使在那些日子里,计算机通常拥有的内存也超过了这个数量,这意味着 ISA 设备无法用于 DMA I/O 操作的一些内存无法使用。这使得任何可能在内存中分配 DMA 缓冲区的设备驱动程序的作者生活变得不便。
为了避免这个问题,内核在 1994 年 11 月的 1.1.68 版本中添加了 `GFP_DMA` 分配标志。直到 1.1.69 版本,这个标志实际上并没有做任何事情,并且在 1.1.73 版本中,Buslogic SCSI 驱动程序添加了支持。这个标志要求内存管理子系统在 ISA 设备能够到达的物理地址范围内分配内存,这样驱动程序作者在分配缓冲区时就不再需要担心。
当时,`GFP_DMA` 只是让分配器跳过不适当位置的内存。这样做是有效的,但生活,正如它的习惯一样,变得更加复杂。内核在早期只能管理不到 1GB 的物理内存;由内核划分的 32 位地址空间,简单地说,不能再包含更多。当然,那近 1GB 的内存应该对任何人来说都足够了。但用户在使用他们为之付费的所有内存方面可能会相当强烈,因此内核开发者面临找到解决方案的压力。
解决方案是“高内存”(high memory),它可以分配给用户进程,但在没有明确的(和临时的)映射步骤的情况下,不能被内核直接寻址。高内存在 1999 年末的 2.3.23pre3 版本中添加,增加了另一种必须考虑的内存类型;内核使用的内存通常不能放在那里。当时,有三种区域:`ZONE_DMA`、`ZONE_NORMAL` 和 `ZONE_HIGHMEM`。
区域系统的一个重要方面是,区域是按照允许简单回退机制的顺序组织的。任何可以从 `ZONE_HIGHMEM` 服务的分配也可以从其他两个区域中的任何一个得到满足,而且 `ZONE_NORMAL` 分配可以回退到 `ZONE_DMA`。这个属性在当前内核中仍然存在。
2006 年 1 月发布的 2.6.15 内核,在 `ZONE_DMA` 和 `ZONE_NORMAL` 之间添加了 `ZONE_DMA32`,以满足只能处理 32 位 DMA 地址的设备的需求。在 2.6.23(2007 年 10 月)中添加了 `ZONE_MOVABLE`,在 4.3(2015 年 11 月)中添加了 `ZONE_DEVICE` 来描述安装在外围设备上的 CPU 可寻址内存。
在一定程度上,所有这些区域都描述了涉及的内存的物理特性,主要与可寻址性有关。`ZONE_MOVABLE` 是个例外。它描述了内存,如果幸运的话,所有内容都可以在需要时移动到其他地方。例如,用户空间页面可以在内存中的其他地方迁移;页表条目将相应更新,用户空间永远不会知道有任何变化。这个区域是为了支持热插拔内存而添加的,这种内存可以随时添加到(或从)系统中。如果系统要从中移除内存,首先将所有内存内容转移到其他地方是相当重要的。
热插拔用例仍然存在,但 `ZONE_MOVABLE` 已经成为内存管理政策的一种表达。如果所有可移动页面都放在内存的同一区域,那么重新分配一些内存来创建大的、物理上连续的范围就相对容易了。所以现在 `ZONE_MOVABLE` 被用作分隔可移动和不可移动分配的方式,并为其他事物提供了重要支持,包括连续内存分配器(contiguous memory allocator, 链接 2)。这里的重要点是 `ZONE_MOVABLE` 可以放置在物理内存的任何位置;它不是由该内存的物理特性驱动的,尤其是在大多数不使用热插拔的系统上。
在当前内核中,区域层次结构是:
-
`ZONE_DMA`
-
`ZONE_DMA32`
-
`ZONE_NORMAL`
-
`ZONE_HIGHMEM`
-
`ZONE_MOVABLE`
-
`ZONE_DEVICE`
注意,不是所有系统都有所有区域;例如,当前的 64 位系统不需要 `ZONE_HIGHMEM`。
明确的策略区域
赵认为,内存区域在辅助分配策略方面可以做得更多。具体来说,他认为可能需要几个其他策略区域(在代码本身中通常称为“虚拟区域”)。为此,他的补丁系列添加了:
-
`ZONE_NOSPLIT`,这是一个区域,其中连续的页块不能分割到给定大小以下。它的存在是为了帮助系统维护大块内存(用于透明大页等),而无需经历持续的压缩过程。
-
`ZONE_NOMERGE` 也具有最小块大小属性,但还禁止将页块合并成更大的组;因此,它只能容纳单一大小的块。
这些区域放置在 `ZONE_MOVABLE` 之后,保持了回退层次结构:`ZONE_NOMERGE` 分配也可以从 `ZONE_NOSPLIT` 得到满足,或者如果失败,可以从更低的区域得到满足。这些区域的分配必须是可移动的,因此也可以回退到 `ZONE_MOVABLE`。
这些区域背后的思想是使透明大页的分配尽可能高效。它们将防止大页的分割,这将使内核无需经历重新组装它们的努力。目前进行的压缩工作的大部分可能会变得不必要。
从某种意义上说,这项工作可以被视为那些希望看到 Linux 总体上使用更大页面大小的人和那些担心相关内部碎片成本的人之间的一种妥协。`ZONE_NOMERGE` 为内核创建了接近第二种原生页面大小的东西,在它们有意义的情况下提供更大的页面,同时仍然保持较小页面的可用性。
然而,透明大页仍然可能存在内部碎片问题;一个进程可能分配了这样一个页面,但只使用了其中一小部分内存。当前内核将尝试通过将大页拆回基础页面来应对这种情况,允许未使用的部分在其他地方重新分配。
在(或以上)`ZONE_NOSPLIT` 中的页面显然不能拆分;这正是该区域存在要执行的策略。相反,赵的补丁集引入了“粉碎”大页的概念。如果一个页面被粉碎,其内容将被迁移(复制)到位于合适区域的较小页面中;一旦该过程完成,原始的大页(保持完整)可以分配给其他用途。粉碎比拆分更昂贵;赵认为这是对于一个没有正确使用其内存的进程的适当成本;从变更日志(链接 3)中:“用零售术语来说,购买退货会被收取重新上架费,原始商品可以重新销售”。
`ZONE_NOMERGE` 的另一个声称的优势是,它促进了巨大 vmemmap 优化(HVO)(链接 4),这在 2020 年这里(链接 5)被涵盖。简而言之,这个技巧允许内核回收用于保存大页中许多页面的 `page` 结构的内存。在大量使用大页的系统中,这种优化可以节省大量内存。在当前内核中,HVO 只能与 hugetlbfs 机制一起使用,这种机制不透明,通常只用于特殊情况。然而,`ZONE_NOMERGE` 页面像 hugetlbfs 页面一样组织在固定块中,因此使用 HVO 变得容易。
补丁集处于早期阶段;除其他事项外,它没有任何基准测试结果来显示这种新机制的优势。其他开发者的审查评论才刚刚开始到来;这是一大块工作,需要一些时间来消化。它可能会成为 5 月份的 Linux 存储、文件系统、内存管理和 BPF 峰会(链接 6)的讨论话题。因此,这些新区域可能不会在不久的将来登陆内核,但它们的优势在长期内可能会证明是有说服力的。
[1]: https://lwn.net/ml/linux-mm/20240229183436.4110845-1-yuzhao@google.com/
[2]: https://lwn.net/Articles/486301/
[3]: https://lwn.net/ml/linux-mm/20240229183436.4110845-3-yuzhao@google.com/
[4]: https://www.kernel.org/doc/html/next/mm/vmemmap_dedup.html
[5]: https://lwn.net/Articles/839737/
[6]: https://events.linuxfoundation.org/lsfmmbpf/
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~