3

问题

在尝试减少/消除应用程序中出现轻微页面错误的过程中,我发现了一个令人困惑的现象;也就是说,即使我认为我已经采取了足够的措施来防止页面错误,我也会反复触发对同一地址的写入的次要页面错误。

背景

根据这里的建议,我打电话mlockall将所有当前和未来的页面锁定到内存中。

在我最初的用例(涉及一个相当大的数组)中,我还按照此处的建议通过写入每个元素(或至少写入每个页面)来预先故障数据;虽然我意识到那里的建议是针对运行带有 RT 补丁的内核的用户,但强制写入以阻止 COW/需求分页的一般想法应该仍然适用。

我曾认为这mlockall可以用来防止轻微的页面错误。虽然手册页似乎只保证不会出现重大错误,但各种其他资源(例如上面)表明它也可以用来防止轻微的页面错误。

内核文档似乎也表明了这一点。例如,unevictable-lru.txtpagemap.txt声明mlock()'ed 页面是不可回收的,因此不适合回收。

尽管如此,我还是继续触发了几个小页面错误。

例子

我创建了一个非常精简的示例来说明问题:

#include <sys/mman.h> // mlockall
#include <stdlib.h> // abort

int main(int , char **) {
  int x;

  if (mlockall(MCL_CURRENT | MCL_FUTURE)) abort();

  while (true) {
    asm volatile("" ::: "memory"); // So GCC won't optimize out the write
    x = 0x42;
  }
  return 0;
}

在这里,我反复写信到同一个地址。很容易看到(例如通过cat /proc/[pid]/status | awk '{print $10}'),在初始化完成后很长时间内我仍然有轻微的页面错误。

运行中pfaults.stp包含的脚本的修改版本* systemtap-doc,我记录了每个页面错误的时间、触发错误的地址、触发错误的指令的地址、是否是主要/次要和读/写。在启动和 的初始故障之后mlockall,所有故障都是相同的:尝试写入x触发了次要写入故障。

连续页面错误之间的间隔显示出惊人的模式。对于一次特定的运行,间隔以秒为单位: 2, 4, 4, 4.8, 8.16, 13.87, 23.588, 40.104, 60, 60, 60, 60, 60, 60, 60, 60, 60, ... 这似乎是(大约)指数回退,绝对上限为 1 分钟。

在隔离的 CPU 上运行它没有影响;以更高的优先级运行也不行。但是,以实时优先级运行可以消除页面错误。

问题

  1. 这种行为是预期的吗?
    1a。什么解释了时间?
  2. 有可能防止这种情况吗?

版本

我正在运行带有内核3.13.0-24-generic和 Systemtap 版本的 Ubuntu 14.04 2.3/0.156, Debian version 2.3-1ubuntu1 (trusty)。编译时gcc-4.8没有额外标志的代码,尽管优化级别似乎无关紧要(前提是asm volatile指令保留在原位;否则写入会完全优化)

如果它们被证明是相关的,我很乐意提供更多详细信息(例如,确切的stap脚本、原始输出等)。


*实际上,vm.pagefault我的内核和 systemtap 组合的探针被破坏了,因为它引用了一个不再存在于内核handle_mm_fault函数中的变量,但修复很简单)

4

2 回答 2

2

@fche 提到透明巨大的页面让我走上了正确的轨道。

对我在问题中链接到的内核文档进行的不那么粗心的阅读表明,这mlock不能阻止内核将页面迁移到新的页面框架;实际上,有一整节专门用于迁移 mlocked pages。因此,简单地调用mlock()并不能保证您不会遇到任何轻微的页面错误

有点晚了,我看到这个答案引用了相同的段落并部分回答了我的问题。

内核可能移动页面的原因之一是内存压缩,即内核释放一个大的连续页面块,以便分配一个“巨大的页面”。透明大页面可以轻松禁用;参见例如这个答案

我的特定测试案例是3.13 内核中引入的一些 NUMA 平衡更改的结果。

引用其中链接的 LWN 文章

调度程序将定期扫描每个进程的地址空间,撤销对当前驻留在 RAM 中的页面的所有访问权限。下次受影响的进程尝试访问该内存时,将导致页面错误。调度程序将捕获该错误并恢复对相关页面的访问......

可以通过将进程的 NUMA 策略设置为显式使用某个节点来禁用调度程序的这种行为。这可以使用numactl命令行(例如numactl --membind=0)或调用libnuma库来完成。

编辑: sysctl文档明确说明了关于 NUMA 平衡:

如果目标工作负载已绑定到 NUMA 节点,则应禁用此功能。

这可以通过sysctl -w kernel.numa_balancing=0

页面迁移可能还有其他原因,但这足以满足我的目的。

于 2014-06-03T23:08:15.933 回答
0

只是在这里推测,但也许您看到的是一些正常的内核页面利用率跟踪(甚至可能是 KSM 或 THP 或 cgroup),其中它试图确定有多少页面处于活动状态。探测 mark_page_accessed 函数,例如

于 2014-06-03T13:42:47.657 回答