DAMON 详细用法
DAMON 提供了以下接口供不同用户使用:
- DAMON 用户空间工具:这是为特权用户(如系统管理员)提供的,他们希望使用一个即插即用的人性化接口。使用该工具,用户可以以人性化的方式使用 DAMON 的主要功能。尽管它可能对特殊情况下的高度优化不够适用。更多细节,请参考其使用文档。
- sysfs 接口:这是为特权用户空间程序员提供的,他们希望更优化地使用 DAMON。使用该接口,用户可以通过读取和写入特殊的 sysfs 文件来使用 DAMON 的主要功能。因此,您可以编写和使用自定义的 DAMON sysfs 包装程序,它们读取/写入 sysfs 文件代替您。DAMON 用户空间工具就是这类程序的一个示例。
- debugfs 接口(已弃用!):这与 sysfs 接口几乎相同。由于已弃用,用户应迁移到 sysfs 接口。如果您依赖于此接口并且无法迁移,请将您的用例报告给 damon@lists.linux.dev 和 linux-mm@kvack.org。
- 内核空间编程接口:这是为内核空间程序员提供的。使用该接口,用户可以通过为自己编写内核空间 DAMON 应用程序来灵活高效地利用 DAMON 的每个功能。您甚至可以扩展 DAMON 以适用于各种地址空间。详细信息,请参考接口文档。
sysfs 接口
当定义了 CONFIG_DAMON_SYSFS 时,DAMON sysfs 接口将被构建。它在其 sysfs 目录 <sysfs>/kernel/mm/damon/
下创建多个目录和文件。您可以通过写入和读取该目录下的文件来控制 DAMON。
以下是一个简短的示例,用户可以监视给定工作负载的虚拟地址空间:
# cd /sys/kernel/mm/damon/admin/ # echo 1 > kdamonds/nr_kdamonds && echo 1 > kdamonds/0/contexts/nr_contexts # echo vaddr > kdamonds/0/contexts/0/operations # echo 1 > kdamonds/0/contexts/0/targets/nr_targets # echo $(pidof <workload>) > kdamonds/0/contexts/0/targets/0/pid_target # echo on > kdamonds/0/state
文件层次结构
DAMON sysfs 接口的文件层次结构如下所示。在下图中,父子关系用缩进表示,每个目录都以 / 结尾,每个目录中的文件用逗号(",")分隔。
/sys/kernel/mm/damon/admin │ kdamonds/nr_kdamonds │ │ 0/state,pid │ │ │ contexts/nr_contexts │ │ │ │ 0/avail_operations,operations │ │ │ │ │ monitoring_attrs/ │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us │ │ │ │ │ │ nr_regions/min,max │ │ │ │ │ targets/nr_targets │ │ │ │ │ │ 0/pid_target │ │ │ │ │ │ │ regions/nr_regions │ │ │ │ │ │ │ │ 0/start,end │ │ │ │ │ │ │ │ ... │ │ │ │ │ │ ... │ │ │ │ │ schemes/nr_schemes │ │ │ │ │ │ 0/action │ │ │ │ │ │ │ access_pattern/ │ │ │ │ │ │ │ │ sz/min,max │ │ │ │ │ │ │ │ nr_accesses/min,max │ │ │ │ │ │ │ │ age/min,max │ │ │ │ │ │ │ quotas/ms,bytes,reset_interval_ms │ │ │ │ │ │ │ │ weights/sz_permil,nr_accesses_permil,age_permil │ │ │ │ │ │ │ watermarks/metric,interval_us,high,mid,low │ │ │ │ │ │ │ filters/nr_filters │ │ │ │ │ │ │ │ 0/type,matching,memcg_id │ │ │ │ │ │ │ stats/nr_tried,sz_tried,nr_applied,sz_applied,qt_exceeds │ │ │ │ │ │ │ tried_regions/total_bytes │ │ │ │ │ │ │ │ 0/start,end,nr_accesses,age │ │ │ │ │ │ │ │ ... │ │ │ │ │ │ ... │ │ │ │ ... │ │ ...
ROOT
DAMON sysfs 接口的根目录是 <sysfs>/kernel/mm/damon/
,其中包含一个名为 admin 的目录。该目录包含了用于特权用户空间程序控制 DAMON 的文件。具有根权限的用户空间工具或守护程序可以使用此目录。
kdamonds/
包括请求规范和结果在内的与监视相关的信息被称为 DAMON 上下文。DAMON 使用名为 kdamond 的内核线程执行每个上下文,可以并行运行多个 kdamond。
在 admin 目录下,存在一个名为 kdamonds 的目录,其中包含用于控制 kdamonds 的文件。最初,该目录只有一个文件,nr_kdamonds。向该文件写入一个数字(N)会创建编号为 0 到 N-1 的子目录。每个目录代表一个 kdamond。
kdamonds/<N>/
在每个 kdamond 目录中,存在两个文件(state 和 pid)和一个目录(contexts)。
读取 state 返回 kdamond 当前是否正在运行的状态,如果正在运行则返回 on,否则返回 off。向 state 文件写入 on 或 off 可以改变 kdamond 的状态。向 state 文件写入 commit 会使 kdamond 重新读取 sysfs 文件中除 state 文件外的用户输入。向 state 文件写入 update_schemes_stats 会更新 kdamond 的每个基于 DAMON 的操作方案的 stats 文件的内容。有关 stats 的详细信息,请参考 stats 部分。
向 state 文件写入 update_schemes_tried_regions 会更新 kdamond 的每个基于 DAMON 的操作方案的已尝试区域目录。向 state 文件写入 update_schemes_tried_bytes 只会更新 .../tried_regions/total_bytes 文件。向 state 文件写入 clear_schemes_tried_regions 会清除 kdamond 的每个基于 DAMON 的操作方案的已尝试区域目录。有关基于 DAMON 的操作方案的已尝试区域目录的详细信息,请参考 tried_regions 部分。
如果状态为 on,则读取 pid 会显示 kdamond 线程的 PID。
contexts 目录包含了控制此 kdamond 将执行的监视上下文的文件。
kdamonds/<N>/contexts/
最初,该目录只有一个文件,nr_contexts。向该文件写入一个数字(N)会创建编号为 0 到 N-1 的子目录。每个目录代表一个监视上下文。目前,每个 kdamond 仅支持一个上下文,因此只能向该文件写入 0 或 1。
contexts/<N>/
在每个上下文目录中,存在两个文件(avail_operations 和 operations)和三个目录(monitoring_attrs、targets 和 schemes)。
DAMON 支持多种类型的监视操作,包括虚拟地址空间和物理地址空间的监视。通过读取 avail_operations 文件,您可以获取当前运行内核上设置的可用监视操作列表。根据内核配置,该文件将列出以下关键字中的一些或全部。
- vaddr: 监视特定进程的虚拟地址空间
- fvaddr: 监视固定虚拟地址范围
- paddr: 监视系统的物理地址空间
有关监视目标区域的监视操作集之间的详细差异,请参考 regions sysfs 目录。
您可以通过向 avail_operations 文件中列出的关键字之一写入并从 operations 文件中读取,来设置和获取 DAMON 将为上下文使用的监视操作类型。
contexts/<N>/monitoring_attrs/
用于指定监视属性的文件,包括监视的所需质量和效率,位于 monitoring_attrs 目录中。具体来说,该目录中存在 intervals 和 nr_regions 两个目录。
在 intervals 目录下,存在三个文件,用于 DAMON 的采样间隔(sample_us)、聚合间隔(aggr_us)和更新间隔(update_us)。您可以通过向这些文件写入和读取微秒值来设置和获取数值。
在 nr_regions 目录下,存在两个文件,用于 DAMON 的监视区域的下限和上限(分别为 min 和 max),用于控制监视开销。您可以通过向这些文件写入和读取数值来设置和获取数值。
有关间隔和监视区域范围的更多详细信息,请参考设计文档(Design)。
contexts/<N>/targets/
最初,该目录只有一个文件,nr_targets。向该文件写入一个数字(N)会创建编号为 0 到 N-1 的子目录。每个目录代表一个监视目标。
在每个目标目录中,存在一个文件(pid_target)和一个目录(regions)。
如果您向 contexts/<N>/operations
写入了 vaddr,每个目标应该是一个进程。您可以通过向 pid_target 文件写入进程的 PID 来指定进程给 DAMON。
targets/<N>/regions
当使用 vaddr 监视操作集时(向 contexts/<N>/operations
写入了 vaddr),DAMON 会自动设置和更新监视目标区域,以便覆盖目标进程的整个内存映射。但是,用户可能希望将初始监视区域设置为特定地址范围。
相反,当使用 fvaddr 或 paddr 监视操作集时(向 contexts/<N>/operations
写入了 fvaddr 或 paddr),DAMON 不会自动设置和更新监视目标区域。因此,在这些情况下,用户应该自行设置监视目标区域。
对于这种情况,用户可以通过向此目录下的文件写入适当的值,显式地设置初始监视目标区域。
最初,该目录只有一个文件,nr_regions。向该文件写入一个数字(N)会创建编号为 0 到 N-1 的子目录。每个目录代表一个初始监视目标区域。
regions/<N>/
在每个区域目录中,您会找到两个文件(start 和 end)。您可以通过分别向这些文件写入和读取来设置和获取初始监视目标区域的起始和结束地址。
每个区域不应与其他区域重叠。目录 N 的结束应等于或小于目录 N+1 的开始。
contexts/<N>/schemes/
这是基于 DAMON 的操作方案(DAMOS)的目录。用户可以通过读取和写入此目录下的文件来获取和设置操作方案。
在开始时,此目录只有一个文件,nr_schemes。向该文件写入一个数字(N)会创建编号为 0 到 N-1 的子目录。每个目录代表一个基于 DAMON 的操作方案。
schemes/<N>/
在每个操作方案目录中,存在五个目录(access_pattern、quotas、watermarks、filters、stats 和 tried_regions)和一个文件(action)。
action 文件用于设置和获取操作方案的动作。可以向该文件写入关键字,并从中读取它们的含义。请注意,每个动作的支持取决于正在运行的 DAMON 操作集实现。
- willneed: 对具有 MADV_WILLNEED 的区域调用 madvise()。由 vaddr 和 fvaddr 操作集支持。
- cold: 对具有 MADV_COLD 的区域调用 madvise()。由 vaddr 和 fvaddr 操作集支持。
- pageout: 对具有 MADV_PAGEOUT 的区域调用 madvise()。由 vaddr、fvaddr 和 paddr 操作集支持。
- hugepage: 对具有 MADV_HUGEPAGE 的区域调用 madvise()。由 vaddr 和 fvaddr 操作集支持。
- nohugepage: 对具有 MADV_NOHUGEPAGE 的区域调用 madvise()。由 vaddr 和 fvaddr 操作集支持。
- lru_prio: 优先考虑区域在其 LRU 列表上。由 paddr 操作集支持。
- lru_deprio: 降低区域在其 LRU 列表上的优先级。由 paddr 操作集支持。
- stat: 仅对统计数据进行计数,不执行其他操作。由所有操作集支持。
schemes/<N>/access_pattern/
这是给定基于 DAMON 的操作方案的目标访问模式的目录。
在 access_pattern 目录下,有三个目录(sz、nr_accesses 和 age),每个目录下都有两个文件(min 和 max)。您可以通过向和从 sz、nr_accesses 和 age 目录下的 min 和 max 文件进行写入和读取,来设置和获取给定方案的访问模式。请注意,min 和 max 形成一个闭区间。
schemes/<N>/quotas/
这是给定基于 DAMON 的操作方案的配额目录。
在配额目录下,有三个文件(ms、bytes、reset_interval_ms)和一个名为 weights 的子目录,该子目录中有三个文件(sz_permil、nr_accesses_permil 和 age_permil)。
您可以通过向这三个文件分别写入值来设置毫秒级的时间配额、字节级的大小配额和毫秒级的重置间隔。然后,DAMON 尝试仅在时间配额毫秒内对访问模式的内存区域应用操作,并在重置间隔内仅对字节级内存区域应用操作。将 ms 和 bytes 都设置为零会禁用配额限制。
您还可以通过向 weights 目录下的三个文件写入值,以设置每千单位的大小、访问频率和年龄的优先权权重。
schemes/<N>/watermarks/
这是给定基于 DAMON 的操作方案的水印目录。
在水印目录下,有五个文件(metric、interval_us、high、mid 和 low),用于设置度量标准、度量标准的检查时间间隔和三个水印的值。您可以通过向这五个文件分别写入值来设置和获取这些数值。
可以写入 metric 文件的关键字及其含义如下。
- none: 忽略水印
- free_mem_rate: 系统的空闲内存率(每千)
时间间隔应以微秒为单位写入。
schemes/<N>/filters/
这是给定基于 DAMON 的操作方案的过滤器目录。
在开始时,此目录只有一个文件,nr_filters。向该文件写入一个数字(N)会创建编号为 0 到 N-1 的子目录。每个目录代表一个过滤器。过滤器按数字顺序进行评估。
每个过滤器目录包含六个文件,分别为 type、matching、memcg_path、addr_start、addr_end 和 target_idx。对于 type 文件,您可以写入四个特殊关键字之一:anon 代表匿名页面,memcg 代表特定内存 cgroup,addr 代表特定地址范围(一个开放区间),target 代表特定的 DAMON 监视目标过滤。在内存 cgroup 过滤的情况下,您可以通过向 memcg_path 文件写入感兴趣的内存 cgroup 的路径来指定内存 cgroup。在地址范围过滤的情况下,您可以分别向 addr_start 和 addr_end 文件写入范围的起始和结束地址。对于 DAMON 监视目标过滤,您可以通过向 target_idx 文件写入目标在 DAMON 上下文的监视目标列表中的索引。您可以向 matching 文件写入 Y 或 N,以过滤出与类型匹配或不匹配的页面。然后,方案的操作将不会应用于指定要过滤出的页面。
例如,以下限制了 DAMOS 操作仅应用于所有内存 cgroup 除了 /having_care_already 中的非匿名页面:
# echo 2 > nr_filters # # 过滤掉匿名页面 echo anon > 0/type echo Y > 0/matching # # 进一步过滤掉除 '/having_care_already' 外的所有 cgroup echo memcg > 1/type echo /having_care_already > 1/memcg_path echo N > 1/matching
请注意,当使用 paddr 实现 <sysfs_contexts> 时,目前仅支持 anon 和 memcg 过滤器。
此外,通过 addr 或 target 过滤器过滤出的内存区域不会计入方案尝试过的区域,而通过其他类型过滤器过滤出的区域会计入方案尝试过的区域。这种差异会应用于统计信息和尝试的区域。
schemes/<N>/stats/
DAMON 统计了每个方案尝试应用的区域的总数和字节数,每个方案成功应用的区域的两个数字,以及配额限制超出的总数。这些统计数据可用于在线分析或调整方案。
您可以通过读取 stats 目录下的文件(nr_tried、sz_tried、nr_applied、sz_applied 和 qt_exceeds)来检索统计信息。这些文件不会实时更新,因此您应该要求 DAMON sysfs 接口通过向相关的 kdamonds/<N>/state
文件写入特殊关键字 update_schemes_stats 来更新统计信息文件的内容。
schemes/<N>/tried_regions/
该目录最初只有一个文件,total_bytes。
当向相关的 kdamonds/<N>/state
文件写入特殊关键字 update_schemes_tried_regions 时,DAMON 会更新 total_bytes 文件,以便读取它会返回方案尝试区域的总大小,并在此目录下创建从 0 开始的整数命名的目录。每个目录包含有关相应方案操作在下一个聚合间隔期间尝试应用的每个内存区域的详细信息的文件。这些信息包括地址范围、nr_accesses 和区域的年龄。
向相关的 kdamonds/<N>/state
文件写入 update_schemes_tried_bytes 将仅更新 total_bytes 文件,而不会创建子目录。
当向相关的 kdamonds/<N>/state
文件写入另一个特殊关键字 clear_schemes_tried_regions 时,这些目录将被移除。
该目录的预期用途是调查方案的行为,并进行类似查询的高效数据访问监视结果检索。特别是对于后一种用例,用户可以将操作设置为 stat,并将访问模式设置为他们感兴趣的模式,以便进行查询。
tried_regions/<N>/
在每个区域目录中,您会找到四个文件(start、end、nr_accesses 和 age)。读取这些文件将显示相应基于 DAMON 的操作方案尝试应用的区域的起始和结束地址、nr_accesses 和年龄。
示例
以下命令应用了一个方案,即“如果大小在 [4KiB, 8KiB] 范围内的内存区域在聚合间隔期间的访问次数在 [0, 5] 之间,且在 [10, 20] 的聚合间隔期间内,将该区域进行页面交换。对于页面交换,每秒最多使用 10 毫秒,并且每秒不要进行超过 1GiB 的页面交换。在这些限制下,首先对年龄较长的内存区域进行页面交换。此外,每 5 秒检查系统的空闲内存率,当空闲内存率低于 50% 时开始监视和进行页面交换,但如果空闲内存率大于 60% 或低于 30% 时停止页面交换”。
# cd <sysfs>/kernel/mm/damon/admin # # populate directories # echo 1 > kdamonds/nr_kdamonds; echo 1 > kdamonds/0/contexts/nr_contexts; # echo 1 > kdamonds/0/contexts/0/schemes/nr_schemes # cd kdamonds/0/contexts/0/schemes/0 # # set the basic access pattern and the action # echo 4096 > access_pattern/sz/min # echo 8192 > access_pattern/sz/max # echo 0 > access_pattern/nr_accesses/min # echo 5 > access_pattern/nr_accesses/max # echo 10 > access_pattern/age/min # echo 20 > access_pattern/age/max # echo pageout > action # # set quotas # echo 10 > quotas/ms # echo $((1024*1024*1024)) > quotas/bytes # echo 1000 > quotas/reset_interval_ms # # set watermark # echo free_mem_rate > watermarks/metric # echo 5000000 > watermarks/interval_us # echo 600 > watermarks/high # echo 500 > watermarks/mid # echo 300 > watermarks/low
请注意,强烈建议使用用户空间工具(如 damo)来操作,而不是手动读写上述文件。上述内容仅供示例参考。
跟踪点用于监控结果
用户可以通过 tried_regions 目录或跟踪点 damon:damon_aggregated 获取监控结果。虽然 tried_regions 目录对于获取快照很有用,但跟踪点对于获取完整的结果记录很有用。在监控打开的情况下,您可以记录跟踪点事件,并使用像 perf 这样支持跟踪点的工具显示结果。例如:
# echo on > monitor_on # perf record -e damon:damon_aggregated & # sleep 5 # kill 9 $(pidof perf) # echo off > monitor_on # perf script