问题
在尝试减少/消除应用程序中出现轻微页面错误的过程中,我发现了一个令人困惑的现象;也就是说,即使我认为我已经采取了足够的措施来防止页面错误,我也会反复触发对同一地址的写入的次要页面错误。
背景
根据这里的建议,我打电话mlockall
将所有当前和未来的页面锁定到内存中。
在我最初的用例(涉及一个相当大的数组)中,我还按照此处的建议通过写入每个元素(或至少写入每个页面)来预先故障数据;虽然我意识到那里的建议是针对运行带有 RT 补丁的内核的用户,但强制写入以阻止 COW/需求分页的一般想法应该仍然适用。
我曾认为这mlockall
可以用来防止轻微的页面错误。虽然手册页似乎只保证不会出现重大错误,但各种其他资源(例如上面)表明它也可以用来防止轻微的页面错误。
内核文档似乎也表明了这一点。例如,unevictable-lru.txt和pagemap.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 上运行它没有影响;以更高的优先级运行也不行。但是,以实时优先级运行可以消除页面错误。
问题
- 这种行为是预期的吗?
1a。什么解释了时间? - 有可能防止这种情况吗?
版本
我正在运行带有内核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
函数中的变量,但修复很简单)