设计
整体架构
DAMON 子系统配置了三层,包括
- 操作集:实现了依赖于给定监控目标地址空间和可用软硬件原语的 DAMON 基本操作,
- 核心:在操作集层之上实现了核心逻辑,包括监控开销/准确性控制和访问感知系统操作,
- 模块:在核心层之上为各种目的实现了内核模块,为用户空间提供接口。
可配置的操作集
对于数据访问监控和额外的低级工作,DAMON 需要一组实现特定操作的实现,这些操作依赖于给定目标地址空间并针对其进行了优化。另一方面,准确性和开销的权衡机制,即 DAMON 的核心逻辑,位于纯逻辑空间中。DAMON 将这两部分分别放在不同的层中,即 DAMON 操作集和 DAMON 核心逻辑层。它进一步定义了层间的接口,以允许使用核心逻辑配置各种操作集。
由于这种设计,用户可以通过配置核心逻辑来扩展 DAMON 以适用于任何地址空间。如果没有合适的操作集可用,用户可以自行实现一个。
例如,物理内存、虚拟内存、交换空间、特定进程的这些,NUMA 节点、文件和后备内存设备都是可以支持的。此外,如果一些架构或设备支持特殊优化的访问检查原语,那么这些也将很容易进行配置。
可编程模块
DAMON 的核心层被实现为一个框架,并向所有内核空间组件(如子系统和模块)公开其应用程序编程接口。对于 DAMON 的常见用例,DAMON 子系统提供了内核模块,这些模块是在核心层之上使用 API 构建的,用户空间的最终用户可以轻松使用。
操作集层
监控操作分为两部分:
- 为地址空间确定监控目标地址范围。
- 对目标空间中特定地址范围进行访问检查。
DAMON 目前提供了针对物理和虚拟地址空间的操作实现。下面的两个小节描述了它们的工作原理。
基于 VMA 的目标地址范围构建
这仅适用于虚拟地址空间监控操作的实现。对于物理地址空间,它要求用户手动设置监控目标地址范围。
进程的超大虚拟地址空间中只有一小部分被映射到物理内存并被访问。因此,跟踪未映射的地址区域只是浪费。然而,由于 DAMON 可以使用自适应区域调整机制处理一定程度的噪声,不严格要求跟踪每个映射,但在某些情况下可能会带来很高的开销。也就是说,在监控目标内部太大的未映射区域应该被移除,以免占用自适应机制的时间。
出于这个原因,该实现将复杂的映射转换为覆盖地址空间的每个映射区域的三个不同区域。三个区域之间的两个间隙是给定地址空间中最大的未映射区域。在大多数情况下,两个最大的未映射区域分别是堆和最上面的 mmap() 区域之间的间隙,以及最下面的 mmap() 区域和栈之间的间隙。因为这些间隙在通常的地址空间中异常巨大,排除这些将足以做出合理的权衡。下面详细描述了这一点:
<堆> <大的未映射区域 1> <最上面的 mmap() 区域> (小的 mmap() 区域和 munmap() 区域) <最下面的 mmap() 区域> <大的未映射区域 2> <栈>
基于 PTE Accessed 位的访问检查
物理和虚拟地址空间的两种实现都使用 PTE Accessed 位进行基本访问检查。唯一的区别在于如何从地址中找到相关的 PTE Accessed 位。虚拟地址的实现会遍历地址的目标任务的页表,而物理地址的实现会遍历与地址有映射的每个页表。通过这种方式,实现会找到并清除下一个采样目标地址的位,并检查在一个采样周期后这些位是否再次被设置。这可能会干扰使用 Accessed 位的其他内核子系统,即空闲页跟踪和回收逻辑。DAMON 不会采取任何措施来避免干扰空闲页跟踪,因此处理干扰是系统管理员的责任。然而,它通过使用 PG_idle 和 PG_young 页标志解决了与回收逻辑的冲突。
核心逻辑
监控
以下四个部分描述了 DAMON 核心机制和五个监控属性,采样间隔、聚合间隔、更新间隔、区域最小数量和区域最大数量。
访问频率监控
DAMON 的输出显示了在给定持续时间内页面被多频繁访问。访问频率的分辨率由设置的采样间隔和聚合间隔控制。具体来说,DAMON 每个采样间隔检查每个页面的访问,并对结果进行聚合。换句话说,它统计了每个页面的访问次数。在每个聚合间隔过后,DAMON 调用之前由用户注册的回调函数,以便用户可以读取聚合结果,然后清除结果。下面是一个简单的伪代码描述:
while 监控开启: for 页面 in 监控目标: if 被访问(页面): nr_accesses[页面] += 1 if time() % 聚合间隔 == 0: for 回调 in 用户注册的回调函数: 回调(监控目标, nr_accesses) for 页面 in 监控目标: nr_accesses[页面] = 0 sleep(采样间隔)
这种机制的监控开销会随着目标工作负载的增加而任意增加。
基于区域的采样
为了避免监控开销的无限增加,DAMON 将假定具有相同访问频率的相邻页面分组为一个区域。只要保持这个假设(区域中的页面具有相同的访问频率),只需要检查一个区域中的一个页面。因此,对于每个采样间隔,DAMON 随机选择每个区域中的一个页面,等待一个采样间隔,检查页面在此期间是否被访问,并在是的情况下增加区域的访问频率。因此,通过设置区域的数量,可以控制监控开销。DAMON 允许用户设置区域的最小和最大数量以进行权衡。
然而,如果假设不能得到保证,这种方案就无法保持输出的质量。
自适应区域调整
即使初始的监控目标区域被很好地构建以满足假设(相同区域中的页面具有相似的访问频率),数据访问模式也可能会动态改变。这将导致监控质量下降。为了尽可能保持假设,DAMON 根据它们的访问频率自适应地合并和分割每个区域。
对于每个聚合间隔,它比较相邻区域的访问频率,如果频率差异很小,则合并这些区域。然后,在报告和清除每个区域的聚合访问频率之后,如果分割后总区域数量不会超过用户指定的最大区域数量,它将把每个区域分割成两个或三个区域。
通过这种方式,DAMON 在保持用户设置的权衡的同时提供了最大的努力质量和最小的开销。
年龄跟踪
通过分析监控结果,用户还可以了解区域当前访问模式的持续时间。这可以用于更好地理解访问模式。例如,可以利用频率和最近性的页面放置算法来实现。为了更容易进行这种访问模式的持续期分析,DAMON 在每个区域中还维护了一个称为年龄的计数器。对于每个聚合间隔,DAMON 检查区域的大小和访问频率(nr_accesses)是否发生了显著变化。如果是,计数器被重置为零。否则,计数器增加。
动态目标空间更新处理
监控目标地址范围可能会动态变化。例如,虚拟内存可能会动态映射和取消映射。物理内存可能会热插拔。
由于这些变化在某些情况下可能非常频繁,DAMON 允许监控操作检查动态变化,包括内存映射变化,并将其应用于监控操作相关的数据结构,比如抽象的监控目标内存区域,每隔用户指定的时间间隔(更新间隔)。
操作方案
数据访问监控的一个常见目的是基于访问的系统效率优化。例如,
将未访问超过两分钟的内存区域换出页面
或者
对于大于 2 MiB 并且在一分钟内显示高访问频率的内存区域使用 THP。
对于这样的方案,一个直接的方法是基于配置文件的优化。也就是说,使用 DAMON 获取工作负载或系统的数据访问监控结果,通过对监控结果进行分析找到具有特殊特征的内存区域,并对这些区域进行系统操作更改。这些更改可以通过修改或提供建议给软件(应用程序和/或内核),或重新配置硬件来实现。可以使用离线和在线方法。
在这些方法中,在运行时向内核提供建议将是灵活和有效的,因此被广泛使用。然而,实施这样的方案可能会带来不必要的冗余和低效。如果感兴趣的类型是常见的,那么分析可能是多余的。在内核和用户空间之间交换包括监控结果和操作建议的信息可能是低效的。
为了让用户通过卸载工作来减少这种冗余和低效,DAMON 提供了一个名为基于数据访问监控的操作方案(DAMOS)的功能。它允许用户以高层次指定他们所需的方案。对于这样的规范,DAMON 开始监控,找到具有感兴趣访问模式的区域,并在找到后立即对这些区域应用用户期望的操作。
操作动作
用户希望应用于他们感兴趣的区域的管理操作。例如,换出页面、优先选择下一个回收受害者、建议 khugepaged 合并或分割,或者什么都不做,只是收集区域的统计信息。
支持的操作列表在 DAMOS 中定义,但每个操作的实现在 DAMON 操作集层中,因为实现通常依赖于监控目标地址空间。例如,用于换出特定虚拟地址范围的代码将与用于物理地址范围的代码不同。并且监控操作的实现集不一定支持列表中的所有操作。因此,特定 DAMOS 操作的可用性取决于选择要一起使用的操作集。
将操作应用于区域被视为改变区域的特征。因此,当对区域应用操作时,DAMOS 会重置区域的年龄。
目标访问模式
方案兴趣的访问模式。这些模式是根据DAMON的监测结果构建的,具体包括大小、访问频率和年龄。用户可以通过设置这三个属性的最小和最大值来描述他们感兴趣的访问模式。如果一个区域的三个属性在这些范围内,DAMOS将把它分类为方案感兴趣的区域之一。
配额
DAMOS的上限开销控制功能。如果目标访问模式没有得到适当调整,DAMOS可能会产生很高的开销。例如,如果找到一个具有感兴趣的访问模式的巨大内存区域,将方案的操作应用于该巨大区域的所有页面可能会消耗不可接受的大量系统资源。通过调整访问模式来防止这种问题可能是具有挑战性的,特别是如果工作负载的访问模式非常动态。
为了缓解这种情况,DAMOS提供了一个称为配额的上限开销控制功能。它允许用户指定DAMOS在应用操作时可以使用的时间上限,和/或在用户指定的时间段内可以应用操作的最大内存区域字节数。
优先级
在配额下做出良好决策的机制。如果由于配额的限制,操作无法应用于所有感兴趣的区域,DAMOS将优先考虑区域并仅将操作应用于具有足够高优先级的区域,以确保不超过配额。
优先级机制对于每个操作应该是不同的。例如,对于页面置换方案操作,很少访问(较冷)的内存区域将被优先考虑。相反,对于巨大页面合并方案操作,较冷的区域将被降低优先级。因此,每个操作的优先级机制都在每个DAMON操作集中实现,与操作一起。
尽管实现由DAMON操作集决定,但通常会使用区域的访问模式属性来计算优先级。一些用户希望机制能够根据他们的特定情况进行个性化。例如,一些用户希望机制更加重视最近性(年龄)而不是访问频率(nr_accesses)。DAMOS允许用户指定每个访问模式属性的权重,并将信息传递给底层机制。然而,如何以及是否尊重这些权重取决于底层优先级机制的实现。
水印
条件DAMOS(停用)激活自动化。用户可能只希望在特定情况下运行DAMOS。例如,当有足够的空闲内存保证时,运行主动回收方案只会消耗不必要的系统资源。为了避免这种消耗,用户需要手动监测一些指标,如空闲内存比例,并打开或关闭DAMON/DAMOS。
DAMOS允许用户使用三个水印来卸载这些工作。它允许用户配置感兴趣的指标以及三个水印值,即高、中和低。如果指标的值超过高水印或低于低水印,方案将被停用。如果指标低于中水印但高于低水印,方案将被激活。如果所有方案都被水印停用,监测也将被停用。在这种情况下,DAMON工作线程仅定期检查水印,因此几乎没有开销。
过滤器
基于非访问模式的目标内存区域过滤。如果用户运行自编写的程序或使用良好的分析工具,他们可能会了解一些内核不知道的信息,例如未来的访问模式或特定类型内存的特殊要求。例如,某些用户可能只知道匿名页面会影响他们程序的性能。他们还可以拥有一个延迟关键进程的列表。
为了让用户使用这些特殊知识优化DAMOS方案,DAMOS提供了一个称为DAMOS过滤器的功能。该功能允许用户为每个方案设置任意数量的过滤器。每个过滤器指定目标内存的类型,以及它是否应该排除该类型的内存(过滤掉),或者只包括该类型的内存(过滤保留)。
目前,该功能支持匿名页面、内存cgroup、地址范围和DAMON监测目标类型的过滤器。某些过滤器目标类型需要额外的参数。内存cgroup过滤器类型要求用户指定过滤器的内存cgroup的文件路径。地址范围类型要求指定范围的起始和结束地址。DAMON监测目标类型要求从上下文的监测目标列表中指定目标的索引。因此,用户可以将特定方案仅应用于匿名页面、非匿名页面、特定cgroup的页面、除特定cgroup之外的所有页面、特定地址范围的页面、特定DAMON监测目标的页面,以及这些的任意组合。
为了高效处理过滤器,地址范围和DAMON监测目标类型的过滤器由核心层处理,而其他过滤器由操作集处理。如果一个内存区域被核心层处理的过滤器过滤掉,它不会被计算为方案已尝试该区域。相反,如果一个内存区域被操作集层处理的过滤器过滤掉,它将被计算为方案已尝试该区域。计算上的差异会导致统计数据的变化。
应用程序编程接口
用于内核空间数据访问感知应用程序的编程接口。DAMON是一个框架,它本身不执行任何操作。相反,它只帮助其他内核组件(如子系统和模块)利用DAMON的核心功能构建其数据访问感知应用程序。为此,DAMON通过其应用程序编程接口(即include/linux/damon.h)向其他内核组件公开其所有功能。有关接口的详细信息,请参阅API文档。
模块
因为DAMON的核心是一个用于内核组件的框架,它不提供与用户空间的直接接口。这样的接口应该由每个DAMON API用户内核组件自己实现。DAMON子系统本身实现了这样的DAMON API用户模块,用于通用的DAMON控制和特定目的的数据访问感知系统操作,并为用户空间提供稳定的应用程序二进制接口(ABI)。用户空间可以使用这些接口构建高效的数据访问感知应用程序。
通用目的用户界面模块
提供用户空间ABI以供运行时通用DAMON使用的DAMON模块。
DAMON用户界面模块,即“DAMON sysfs接口”和“DAMON debugfs接口”,是提供给用户空间的DAMON API用户内核模块,它们提供了与用户空间的ABI。请注意,DAMON debugfs接口目前已被弃用。
与许多其他ABI一样,这些模块在sysfs和debugfs上创建文件,允许用户通过写入和读取文件来指定其请求并从DAMON获取答案。作为对此类I/O的响应,DAMON用户界面模块通过DAMON API控制DAMON并按用户请求检索结果,并将结果返回给用户空间。
这些ABI设计用于用户空间应用程序开发,而不是人类用户的手指。建议人类用户使用这些用户空间工具。其中一个使用Python编写的用户空间工具可在Github(https://github.com/awslabs/damo)、Pypi(https://pypistats.org/packages/damo)和Fedora(https://packages.fedoraproject.org/pkgs/python-damo/damo/)上找到。
有关接口的详细信息,请参阅ABI文档。
特定目的的访问感知内核模块
DAMON模块提供了特定目的的DAMON使用的用户空间ABI。
DAMON sysfs/debugfs用户界面用于在运行时完全控制所有DAMON功能。对于每个特定目的的系统范围数据访问感知系统操作,例如主动回收或LRU列表平衡,界面可以通过去除特定目的的不必要旋钮来简化,并扩展到引导时间甚至编译时间控制。对于特定目的,DAMON控制参数的默认值也需要进行优化。
为了支持这种情况,提供了更简单和优化的用户空间接口的更多DAMON API用户内核模块。目前,提供了两个模块,用于主动回收和LRU列表操作。有关详细信息,请阅读这些模块的使用文档(基于DAMON的回收和基于DAMON的LRU列表排序)。