空闲页面跟踪

动机

空闲页面跟踪功能允许跟踪工作负载正在访问哪些内存页面,以及哪些页面处于空闲状态。此信息对于估计工作负载的工作集大小很有用,而工作集大小反过来可以在配置工作负载参数、设置内存 cgroup 限制或决定将工作负载放置在计算集群中的何处时加以考虑。

它通过 CONFIG_IDLE_PAGE_TRACKING=y 启用。

用户 API

空闲页面跟踪 API 位于 /sys/kernel/mm/page_idle。目前,它仅包含一个读写文件,/sys/kernel/mm/page_idle/bitmap

该文件实现了一个位图,其中每个位对应一个内存页面。位图由一个 8 字节整数数组表示,PFN #i 处的页面映射到数组元素 #i/64 的位 #i%64,字节顺序为本机顺序。当一位被设置时,相应的页面处于空闲状态。

如果页面自从被标记为空闲后没有被访问过,则认为该页面为空闲状态(有关“访问”的实际含义的更多详细信息,请参见实现细节部分)。要将页面标记为空闲状态,必须通过写入文件来设置对应于该页面的位。写入文件的值将与当前位图值进行 OR 运算。

仅跟踪对用户内存页面的访问。这些页面映射到进程地址空间、页面缓存和缓冲区页面、交换缓存页面。对于其他页面类型(例如 SLAB 页面),尝试将页面标记为空闲状态将被静默忽略,因此这些页面永远不会报告为空闲状态。

对于大页面,空闲标志仅在头页面上设置,因此必须读取 /proc/kpageflags 才能正确计算空闲大页面。

如果未在 8 字节边界上开始读取/写入,或者读取/写入的大小不是 8 字节的倍数,则读取或写入 /sys/kernel/mm/page_idle/bitmap 将返回 -EINVAL。写入此文件超出最大 PFN 将返回 -ENXIO。

也就是说,为了估计工作负载未使用的页面数量,应该

  1. 通过设置 /sys/kernel/mm/page_idle/bitmap 中的相应位,将工作负载的所有页面标记为空闲状态。如果工作负载由进程表示,则可以通过读取 /proc/pid/pagemap 来找到这些页面,如果工作负载放置在内存 cgroup 中,则可以通过使用 /proc/kpagecgroup 过滤掉外部页面来找到这些页面。

  2. 等待直到工作负载访问其工作集。

  3. 读取 /sys/kernel/mm/page_idle/bitmap 并计算设置的位数。如果想忽略某些类型的页面,例如由于它们不可回收而忽略 mlocked 页面,则可以使用 /proc/kpageflags 过滤掉它们。

tools/mm 目录中的 page-types 工具可用于协助完成此操作。如果该工具最初使用适当的选项运行,它会将所有查询的页面标记为空闲状态。然后,该工具的后续运行可以显示哪些页面在过渡期间清除了其空闲标志。

有关 /proc/pid/pagemap/proc/kpageflags/proc/kpagecgroup 的更多信息,请参见 检查进程页表

实现细节

内核内部会跟踪对用户内存页面的访问,以便在内存短缺的情况下首先回收未引用的页面。如果最近通过进程地址空间访问了页面,则认为该页面被引用,在这种情况下,它映射到的一个或多个 PTE 将设置了 Accessed 位,或者由内核显式标记为访问(请参见 mark_page_accessed())。后一种情况发生在

  • 用户空间进程使用系统调用(例如 read(2) 或 write(2))读取或写入页面时

  • 当读取或写入用于存储文件系统缓冲区的页面时,因为进程需要其中存储的文件系统元数据(例如,列出目录树)

  • 当设备驱动程序使用 get_user_pages() 访问页面时

当由于内存回收或超过脏内存限制而将脏页面写入交换空间或磁盘时,不会将其标记为已引用。

空闲内存跟踪功能添加了一个新的页面标志,即空闲标志。此标志通过写入 /sys/kernel/mm/page_idle/bitmap 手动设置(请参见用户 API部分),并且只要按照上述定义引用页面,就会自动清除。

当页面被标记为空闲状态时,必须清除其映射到的所有 PTE 中的 Accessed 位,否则我们将无法检测到来自进程地址空间的对该页面的访问。为了避免与回收器发生冲突,如上所述,回收器使用 Accessed 位来提升活跃引用的页面,因此引入了一个额外的页面标志,即 Young 标志。当由于设置或更新页面的空闲标志而清除 PTE Accessed 位时,将在页面上设置 Young 标志。回收器将 Young 标志视为额外的 PTE Accessed 位,因此会将此类页面视为已引用。

由于空闲内存跟踪功能基于内存回收器逻辑,因此它仅适用于 LRU 列表中的页面,其他页面将被静默忽略。这意味着如果用户内存页面被隔离,它将忽略该页面,但由于通常没有太多此类页面,因此应该不会显着影响总体结果。为了不停止扫描空闲页面位图,也可能会跳过锁定的页面。