【发布时间】:2014-06-02 19:12:12
【问题描述】:
问题
在尝试减少/消除应用程序中出现轻微页面错误的过程中,我发现了一个令人困惑的现象;也就是说,即使我认为我已经采取了足够的措施来防止页面错误,我也会反复触发对同一地址的写入的次要页面错误。
背景
根据here 的建议,我致电mlockall 将所有当前和未来的页面锁定到内存中。
在我最初的用例(涉及一个相当大的数组)中,我还按照here 的建议通过写入每个元素(或至少写入每个页面)来预先故障数据;尽管我意识到那里的建议是针对运行带有 RT 补丁的内核的用户,但强制写入以阻止 COW/需求分页的一般想法应该仍然适用。
我曾认为mlockall 可以用来防止轻微的页面错误。虽然手册页似乎只保证不会出现重大错误,但各种其他资源(例如上面)表明它也可以用来防止轻微的页面错误。
内核文档似乎也表明了这一点。例如,unevictable-lru.txt 和pagemap.txt 声明mlock() 的页面是不可回收的,因此不适合回收。
尽管如此,我还是继续触发了几个小页面错误。
示例
我创建了一个非常精简的示例来说明问题:
#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}')在初始化完成后很长时间我仍然有轻微的页面错误。
运行 systemtap-doc 中包含的 pfaults.stp 脚本的修改版本*,我记录了每个页面错误的时间、触发错误的地址、触发错误的指令地址、是否是主要/次要、和读/写。在启动和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 上运行它没有影响;以更高的优先级运行也不行。但是,以实时优先级运行可以消除页面错误。
问题
- 这种行为是预期的吗?
1a。什么解释了时间安排? - 是否可以防止这种情况发生?
版本
我正在运行 Ubuntu 14.04,内核为3.13.0-24-generic,Systemtap 版本为2.3/0.156, Debian version 2.3-1ubuntu1 (trusty)。使用gcc-4.8 编译的代码没有额外的标志,尽管优化级别似乎无关紧要(前提是asm volatile 指令保留在原位;否则写入将完全优化)
如果证明相关,我很乐意提供更多详细信息(例如确切的 stap 脚本、原始输出等)。
*实际上,vm.pagefault 探针因我的内核和 systemtap 组合而损坏,因为它引用了一个不再存在于内核的 handle_mm_fault 函数中的变量,但修复很简单)
【问题讨论】:
标签: linux virtual-memory systemtap