英文

设计

执行模型和数据结构

与监控相关的信息,包括监控请求规范和基于DAMON的操作方案,存储在名为DAMON context 的数据结构中。DAMON使用名为 kdamond 的内核线程执行每个上下文。多个kdamond可以并行运行,用于不同类型的监控。

要了解用户空间如何进行配置和启动/停止DAMON,请参阅DAMON sysfs接口文档。

总体架构

DAMON子系统配置为三层,包括

  • 操作集:实现DAMON的基本操作,这些操作依赖于给定的监控目标地址空间和可用的软件/硬件原语集合,

  • 核心:在操作集层之上实现核心逻辑,包括监控开销/精度控制和访问感知系统操作,以及

  • 模块:在核心层之上实现用于各种目的的内核模块,为用户空间提供接口。

操作集层

对于数据访问监控和其他底层工作,DAMON需要一组针对特定操作的实现,这些操作依赖于给定的目标地址空间并针对其进行了优化。例如,以下两个用于访问监控的操作是与地址空间相关的。

  1. 标识地址空间的监控目标地址范围。

  2. 检查目标空间中特定地址范围的访问情况。

DAMON将这些实现整合在一个名为DAMON操作集的层中,并定义了它与上层之间的接口。上层专门用于DAMON的核心逻辑,包括控制监控精度和开销的机制。

因此,通过配置核心逻辑以使用适当的操作集,DAMON可以轻松地扩展到任何地址空间和/或可用的硬件功能。如果给定目的没有可用的操作集,则可以按照层之间的接口实现新的操作集。

例如,物理内存、虚拟内存、交换空间、特定进程的内存、NUMA节点、文件和后备内存设备都将是可支持的。此外,如果某些架构或设备支持特殊的优化访问检查功能,这些功能将很容易配置。

DAMON目前提供以下三个操作集。以下两个小节描述了它们的工作方式。

  • vaddr:监控特定进程的虚拟地址空间

  • fvaddr:监控固定的虚拟地址范围

  • paddr:监控系统的物理地址空间

要了解用户空间如何通过DAMON sysfs接口进行配置,请参阅文档的operations文件部分。

基于VMA的目标地址范围构建

vaddr DAMON操作集的一种机制,它可以自动初始化和更新监控目标地址区域,以便可以覆盖目标进程的整个内存映射。

此机制仅适用于vaddr操作集。在fvaddrpaddr操作集的情况下,用户需要手动设置监控目标地址范围。

进程的超大虚拟地址空间中只有小部分映射到物理内存并被访问。因此,跟踪未映射的地址区域只是浪费。但是,由于DAMON可以使用自适应区域调整机制来处理一定程度的噪声,因此不严格要求跟踪每个映射,但在某些情况下甚至可能导致较高的开销。也就是说,应删除监控目标内的过大未映射区域,以避免花费时间在自适应机制上。

因此,此实现将复杂的映射转换为三个不同的区域,这些区域覆盖地址空间的每个映射区域。三个区域之间的两个间隙是给定地址空间中两个最大的未映射区域。在大多数情况下,两个最大的未映射区域将是堆和最上面的mmap()区域之间的间隙,以及最下面的mmap()区域和堆栈之间的间隙。由于这些间隙在通常的地址空间中非常大,因此排除这些间隙足以做出合理的折衷。下面详细说明了这一点

<heap>
<BIG UNMAPPED REGION 1>
<uppermost mmap()-ed region>
(small mmap()-ed regions and munmap()-ed regions)
<lowermost mmap()-ed region>
<BIG UNMAPPED REGION 2>
<stack>

基于PTE访问位的访问检查

物理和虚拟地址空间的实现都使用PTE访问位进行基本访问检查。唯一的区别是从地址中查找相关的PTE访问位的方式。虚拟地址的实现遍历目标任务的页表,而物理地址的实现遍历具有到该地址的映射的每个页表。通过这种方式,实现找到并清除下一个采样目标地址的位,并检查在采样周期后是否再次设置这些位。这可能会干扰使用访问位的其他内核子系统,即空闲页跟踪和回收逻辑。DAMON不采取任何措施来避免干扰空闲页跟踪,因此处理干扰是系统管理员的责任。但是,它使用PG_idlePG_young页面标志解决了与回收逻辑的冲突,就像空闲页跟踪一样。

核心逻辑

监控

以下四个部分描述了DAMON的每个核心机制和五个监控属性,采样间隔聚合间隔更新间隔最小区域数最大区域数

要了解用户空间如何通过DAMON sysfs接口设置属性,请参阅文档的monitoring_attrs部分。

访问频率监控

DAMON的输出说明在给定持续时间内,哪些页面被访问的频率如何。访问频率的分辨率通过设置采样间隔聚合间隔来控制。详细来说,DAMON会检查每个页面在每个采样间隔的访问情况,并聚合结果。换句话说,计算每个页面的访问次数。在每个聚合间隔过去后,DAMON会调用先前由用户注册的回调函数,以便用户可以读取聚合结果,然后清除结果。这可以用下面简单的伪代码描述

while monitoring_on:
    for page in monitoring_target:
        if accessed(page):
            nr_accesses[page] += 1
    if time() % aggregation_interval == 0:
        for callback in user_registered_callbacks:
            callback(monitoring_target, nr_accesses)
        for page in monitoring_target:
            nr_accesses[page] = 0
    sleep(sampling interval)

随着目标工作负载大小的增长,此机制的监控开销将任意增加。

基于区域的采样

为了避免开销的无限增长,DAMON将假定具有相同访问频率的相邻页面分组到一个区域中。只要保持该假设(区域中的页面具有相同的访问频率),则只需要检查区域中的一个页面。因此,对于每个采样间隔,DAMON会在每个区域中随机选择一个页面,等待一个采样间隔,检查该页面是否在此期间被访问,如果是,则增加该区域的访问频率计数器。计数器称为区域的 nr_accesses。因此,可以通过设置区域数量来控制监控开销。DAMON允许用户设置折衷方案的最小和最大区域数。

但是,如果不能保证该假设,则此方案无法保持输出质量。

自适应区域调整

即使最初的监控目标区域被构建得很好以满足该假设(同一区域中的页面具有相似的访问频率),数据访问模式也可以动态更改。这将导致监控质量降低。为了尽可能地保持该假设,DAMON会根据每个区域的访问频率自适应地合并和拆分每个区域。

对于每个 聚合间隔,它会比较相邻区域的访问频率(nr_accesses)。如果差异很小,并且两个区域的大小之和小于总区域大小除以 最小区域数,DAMON 将合并这两个区域。如果生成的总区域数仍然高于 最大区域数,它将重复合并操作,同时增加访问频率差异阈值,直到满足区域数量的上限,或者阈值高于可能的最大值(聚合间隔 除以 采样间隔)。然后,在报告并清除每个区域的聚合访问频率后,如果拆分后的总区域数不会超过用户指定的最大区域数,它会将每个区域拆分为两个或三个区域。

通过这种方式,DAMON 在保持用户设置的折衷边界的同时,提供了其尽力而为的质量和最小的开销。

年龄追踪

通过分析监控结果,用户还可以找到区域的当前访问模式持续了多长时间。这可以用于更好地理解访问模式。例如,可以使用频率和最近性的页面放置算法。为了使这种访问模式维护周期分析更容易,DAMON 在每个区域中维护另一个名为 age 的计数器。对于每个 聚合间隔,DAMON 检查区域的大小和访问频率(nr_accesses)是否发生了显著变化。如果是,则计数器重置为零。否则,计数器增加。

动态目标空间更新处理

监控目标地址范围可能会动态更改。例如,可以动态映射和取消映射虚拟内存。可以热插拔物理内存。

由于在某些情况下更改可能非常频繁,DAMON 允许监控操作检查动态更改,包括内存映射更改,并将其应用于与监控操作相关的数据结构,例如抽象的监控目标内存区域,仅在用户指定的时间间隔(更新间隔)内进行。

用户空间可以通过 DAMON sysfs 接口和/或跟踪点获取监控结果。有关更多详细信息,请参阅 DAMOS 尝试的区域监控结果的跟踪点 的文档。

操作方案

数据访问监控的一个常见目的是实现访问感知的系统效率优化。例如,

分页输出超过两分钟未访问的内存区域

或者

对大于 2 MiB 且显示超过一分钟高访问频率的内存区域使用 THP。

这种方案的一种直接方法是基于配置文件的优化。也就是说,使用 DAMON 获取工作负载或系统的数据访问监控结果,通过分析监控结果找到具有特殊特征的内存区域,并对这些区域进行系统操作更改。这些更改可以通过修改软件(应用程序和/或内核)或向其提供建议,或重新配置硬件来实现。离线和在线方法都可以使用。

其中,在运行时向内核提供建议是灵活且有效的,因此被广泛使用。但是,实现此类方案可能会导致不必要的冗余和低效。如果感兴趣的类型很常见,则分析可能是冗余的。内核和用户空间之间交换包括监控结果和操作建议在内的信息可能是低效的。

为了让用户通过卸载工作来减少这种冗余和低效,DAMON 提供了一个称为基于数据访问监控的操作方案 (DAMOS) 的功能。它允许用户在高层次上指定他们期望的方案。对于此类规范,DAMON 开始监控,查找具有感兴趣的访问模式的区域,并为每个用户指定的时间间隔(称为 apply_interval)将用户期望的操作动作应用于这些区域。

要了解用户空间如何通过 DAMON sysfs 接口 设置 apply_interval,请参阅文档的 apply_interval_us 部分。

操作动作

用户希望应用于他们感兴趣的区域的管理动作。例如,分页输出、优先选择下一个回收牺牲品、建议 khugepaged 合并或拆分,或者什么都不做,只是收集区域的统计信息。

支持的操作列表在 DAMOS 中定义,但每个操作的实现都在 DAMON 操作集层中,因为该实现通常取决于监控目标地址空间。例如,分页输出特定虚拟地址范围的代码与物理地址范围的代码不同。并且监控操作实现集并非必须支持列表中的所有操作。因此,特定 DAMOS 操作的可用性取决于选择一起使用的操作集。

支持的操作列表、它们的含义以及支持每个操作的 DAMON 操作集如下所示。

  • willneed: 使用 MADV_WILLNEED 调用区域的 madvise()vaddrfvaddr 操作集支持。

  • cold: 使用 MADV_COLD 调用区域的 madvise()vaddrfvaddr 操作集支持。

  • pageout: 回收区域。vaddrfvaddrpaddr 操作集支持。

  • hugepage: 使用 MADV_HUGEPAGE 调用区域的 madvise()vaddrfvaddr 操作集支持。

  • nohugepage: 使用 MADV_NOHUGEPAGE 调用区域的 madvise()vaddrfvaddr 操作集支持。

  • lru_prio: 在其 LRU 列表上优先排序该区域。 paddr 操作集支持。

  • lru_deprio: 在其 LRU 列表上取消优先排序该区域。 paddr 操作集支持。

  • migrate_hot: 迁移区域,优先考虑较热的区域。 paddr 操作集支持。

  • migrate_cold: 迁移区域,优先考虑较冷的区域。 paddr 操作集支持。

  • stat: 什么都不做,只是计算统计信息。所有操作集都支持。

stat 以外的操作应用于区域被视为更改区域的特征。因此,当任何此类操作应用于区域时,DAMOS 会重置区域的年龄。

要了解用户空间如何通过 DAMON sysfs 接口 设置操作,请参阅文档的 action 部分。

目标访问模式

方案感兴趣的访问模式。这些模式由 DAMON 的监控结果提供的属性构建,特别是大小、访问频率和年龄。用户可以通过设置这三个属性的最小值和最大值来描述他们感兴趣的访问模式。如果区域的三个属性都在范围内,则 DAMOS 将其归类为该方案感兴趣的区域之一。

要了解用户空间如何通过 DAMON sysfs 接口 设置访问模式,请参阅文档的 access_pattern 部分。

配额

DAMOS 上限开销控制功能。如果目标访问模式未正确调整,DAMOS 可能会产生较高的开销。例如,如果找到具有感兴趣的访问模式的巨大内存区域,则将该方案的操作应用于该巨大区域的所有页面可能会消耗不可接受的大量系统资源。通过调整访问模式来防止此类问题可能具有挑战性,尤其是在工作负载的访问模式高度动态的情况下。

为了缓解这种情况,DAMOS 提供了一个称为配额的上限开销控制功能。它允许用户指定 DAMOS 可用于应用操作的时间上限,和/或在用户指定的时间段内可以应用该操作的最大内存区域字节数。

要了解用户空间如何通过 DAMON sysfs 接口 设置基本配额,请参阅文档的 配额 部分。

优先级

一种在配额限制下做出良好决策的机制。当由于配额限制,某个操作无法应用于所有感兴趣的区域时,DAMOS 会优先处理区域,并将该操作仅应用于优先级足够高的区域,以避免超出配额。

对于每个操作,优先级机制应该有所不同。例如,对于页面换出方案操作,应优先考虑很少访问(较冷)的内存区域。相反,对于巨页合并方案操作,应降低较冷区域的优先级。因此,每个操作的优先级机制都与操作一起在每个 DAMON 操作集中实现。

尽管实现方式取决于 DAMON 操作集,但通常使用区域的访问模式属性来计算优先级。一些用户可能希望这些机制针对他们的特定情况进行个性化定制。例如,一些用户可能希望该机制更侧重于最近访问时间(age)而不是访问频率(nr_accesses)。DAMOS 允许用户指定每个访问模式属性的权重,并将该信息传递给底层机制。但是,是否以及如何尊重权重取决于底层优先级机制的实现。

要了解用户空间如何通过 DAMON sysfs 接口 设置优先级权重,请参阅文档的 权重 部分。

目标导向的反馈驱动自动调整

自动反馈驱动的配额调整。用户无需设置绝对配额值,而是可以指定他们感兴趣的指标以及他们希望该指标达到的目标值。然后,DAMOS 会自动调整相应方案的激进程度(即配额)。例如,如果 DAMOS 未能实现目标,则 DAMOS 会自动增加配额。如果 DAMOS 超出目标,则会减少配额。

可以通过三个参数指定目标,即 target_metrictarget_valuecurrent_value。自动调整机制会尝试使 target_metriccurrent_valuetarget_value 相同。目前,提供了两个 target_metric

  • user_input:用户提供的值。用户可以使用他们感兴趣的任何指标作为该值。例如,可以使用空间主工作负载的延迟或吞吐量,系统指标(如可用内存比率或内存压力暂停时间 (PSI))。请注意,在这种情况下,用户应自行显式设置 current_value。换句话说,用户应重复提供反馈。

  • some_mem_psi_us:从上次配额重置到下次配额重置期间测量的系统范围内的 some 内存压力暂停信息(以微秒为单位)。DAMOS 会自行测量,因此用户只需在初始时设置 target_value。换句话说,DAMOS 会进行自我反馈。

要了解用户空间如何通过 DAMON sysfs 接口 设置调整目标指标、目标值和/或当前值,请参阅文档的 配额目标 部分。

水位线

有条件的 DAMOS 激活/停用自动化。用户可能希望仅在特定情况下运行 DAMOS。例如,当保证有足够的可用内存时,运行主动回收方案只会消耗不必要的系统资源。为了避免这种消耗,用户需要手动监视一些指标(例如可用内存比率),并打开或关闭 DAMON/DAMOS。

DAMOS 允许用户使用三个水位线来卸载此类工作。它允许用户配置他们感兴趣的指标以及三个水位线值,即高、中和低。如果指标的值高于高水位线或低于低水位线,则该方案将被停用。如果指标的值低于中水位线但高于低水位线,则该方案将被激活。如果所有方案都被水位线停用,则监视也会被停用。在这种情况下,DAMON 工作线程仅会定期检查水位线,因此几乎不会产生任何开销。

要了解用户空间如何通过 DAMON sysfs 接口 设置水位线,请参阅文档的 水位线 部分。

过滤器

基于非访问模式的目标内存区域过滤。如果用户运行自己编写的程序或拥有良好的分析工具,他们可以知道比内核更多的信息,例如未来的访问模式或对特定类型内存的一些特殊要求。例如,一些用户可能知道只有匿名页面才会影响其程序的性能。他们还可以拥有一个延迟关键进程的列表。

为了让用户使用这些特殊知识优化 DAMOS 方案,DAMOS 提供了一个名为 DAMOS 过滤器的功能。该功能允许用户为每个方案设置任意数量的过滤器。每个过滤器指定目标内存的类型,以及是否应排除该类型的内存(过滤出),或者排除该类型以外的所有内存(过滤入)。

为了高效处理过滤器,某些类型的过滤器由核心层处理,而其他过滤器由操作集处理。因此,在后一种情况下,对过滤器类型的支持取决于 DAMON 操作集。在核心层处理的过滤器的情况下,被过滤器排除的内存区域不会被计为该方案尝试过的区域。相反,如果内存区域被操作集层处理的过滤器过滤,则会被计为该方案尝试过的区域。这种差异会影响统计信息。

目前支持以下类型的过滤器。

  • 匿名页面
    • 应用于包含未存储在文件中的数据的页面。

    • 由操作集层处理。仅 paddr 集支持。

  • 内存 cgroup
    • 应用于属于给定 cgroup 的页面。

    • 由操作集层处理。仅 paddr 集支持。

  • 年轻页面
    • 应用于从方案上次访问检查后访问的页面。

    • 由操作集层处理。仅 paddr 集支持。

  • 地址范围
    • 应用于属于给定地址范围的页面。

    • 由核心逻辑处理。

  • DAMON 监视目标
    • 应用于属于给定 DAMON 监视目标的页面。

    • 由核心逻辑处理。

要了解用户空间如何通过 DAMON sysfs 接口 设置水位线,请参阅文档的 过滤器 部分。

应用程序编程接口

用于内核空间数据访问感知应用程序的编程接口。DAMON 是一个框架,因此它本身不执行任何操作。相反,它仅帮助其他内核组件(如子系统和模块)使用 DAMON 的核心功能构建其数据访问感知应用程序。为此,DAMON 通过其应用程序编程接口(即 include/linux/damon.h)将其所有功能公开给其他内核组件。有关接口的详细信息,请参阅 API 文档

模块

由于 DAMON 的核心是内核组件的框架,因此它不为用户空间提供任何直接接口。此类接口应由每个 DAMON API 用户内核组件实现。DAMON 子系统本身实现了此类 DAMON API 用户模块,这些模块应主要用于通用 DAMON 控制和特殊用途的数据访问感知系统操作,并为用户空间提供稳定的应用程序二进制接口 (ABI)。用户空间可以使用这些接口构建其高效的数据访问感知应用程序。

通用用户界面模块

为运行时通用 DAMON 用途提供用户空间 ABI 的 DAMON 模块。

DAMON 用户界面模块,即“DAMON sysfs 接口”和“DAMON debugfs 接口”,是为用户空间提供 ABI 的 DAMON API 用户内核模块。请注意,DAMON debugfs 接口目前已弃用。

与许多其他 ABI 一样,这些模块会在 sysfs 和 debugfs 上创建文件,允许用户通过写入和读取文件来向 DAMON 指定请求并从 DAMON 获取答案。作为对此类 I/O 的响应,DAMON 用户界面模块会控制 DAMON,并通过 DAMON API 检索用户请求的结果,并将结果返回给用户空间。

这些 ABI 旨在用于用户空间应用程序开发,而不是用于人工操作。建议人工用户使用此类用户空间工具。Github (https://github.com/damonitor/damo)、Pypi (https://pypistats.org/packages/damo) 和 Fedora (https://packages.fedoraproject.org/pkgs/python-damo/damo/) 上提供了此类用 Python 编写的用户空间工具。

有关接口的详细信息,请参阅 ABI 文档

特殊用途的访问感知内核模块

为特定用途的 DAMON 用途提供用户空间 ABI 的 DAMON 模块。

DAMON sysfs/debugfs 用户界面用于在运行时完全控制所有 DAMON 功能。对于每个特殊用途的系统范围数据访问感知系统操作(如主动回收或 LRU 列表平衡),可以通过删除特定用途的不必要旋钮来简化接口,并扩展接口以用于启动时甚至编译时控制。还需要针对该用途优化 DAMON 控制参数的默认值。

为了支持这些情况,提供了更多 DAMON API 用户内核模块,这些模块提供更简单和优化的用户空间接口。目前,提供了两个用于主动回收和 LRU 列表操作的模块。有关更多详细信息,请阅读这些模块的使用文档(基于 DAMON 的回收基于 DAMON 的 LRU 列表排序)。