c++ - 写入映射文件缓冲区时出现轻微页面错误
问题描述
在写入文件由磁盘支持的 mmapped 文件缓冲区时,我注意到轻微的页面错误。
我对mmap的理解是,对于文件映射,页缓存有文件的数据,页表会更新指向页缓存中的文件数据。这意味着在第一次写入 mmapped 缓冲区时,必须更新页表以指向页缓存,我们可能会看到轻微的页错误。但是,正如我下面的基准测试所示,即使在预先对 mmapped 缓冲区进行故障处理之后,我在进行随机写入时仍然会看到轻微的页面错误。
buf
请注意,仅当我在写入 mmapped 缓冲区之间写入随机缓冲区(在下面的基准测试中)时,才会显示这些轻微的页面错误。另请注意,当使用非磁盘支持的 tmpfs 时,这些小页面错误似乎不会发生。
所以我的问题是为什么我们在写入磁盘支持的文件时会看到这些小页面错误?
这是基准:
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <fstream>
#include <sys/mman.h>
#include <sys/resource.h>
int main(int argc, char** argv) {
// Command line parsing.
if (argc != 2) {
std::cout << "Usage: ./bench <path to file>" << std::endl;
exit(1);
}
std::string filepath = argv[1];
// Open and truncate the file to be of size `FILE_LEN`.
int fd = open(filepath.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0664);
const size_t FILE_LEN = (1 << 26); // 64MiB
if (fd < 0) {
std::cout << "open failed: " << strerror(errno) << std::endl;
exit(1);
}
if (ftruncate(fd, FILE_LEN) < 0) {
std::cout << "ftruncate failed: " << strerror(errno) << std::endl;
exit(1);
}
// `mmap` the file and pre-fault it.
char* ptr = static_cast<char*>(mmap(nullptr, FILE_LEN, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, fd, 0));
if(ptr == MAP_FAILED) {
std::cout << "mmap failed: " << strerror(errno) << std::endl;
exit(1);
}
memset(ptr, 'A', FILE_LEN);
// Create a temporary buffer to clear the cache.
constexpr size_t BUF_LEN = (1 << 22); // 4MiB
char* buf = new char[BUF_LEN];
memset(buf, 'B', BUF_LEN);
std::cout << "Opened file " << fd << ", pre faulted " << ptr[rand() % FILE_LEN] << " " << buf[rand() % BUF_LEN]<< std::endl;
// Run the actual benchmark
rusage usage0, usage1;
getrusage(RUSAGE_THREAD, &usage0);
unsigned int x = 0;
for (size_t i = 0; i < 1000; ++i) {
char c = i % 26 + 'a';
const size_t WRITE_SIZE = 1024;
size_t start = i*WRITE_SIZE;
if (start + WRITE_SIZE >= FILE_LEN) {
start %= FILE_LEN;
}
memset(ptr + start, c, WRITE_SIZE);
x += ptr[start];
char d = (c * 142) % 26 + 'a';
for (size_t k = 0; k < BUF_LEN; ++k) {
buf[k] = d;
}
x += buf[int(d)];
}
std::cout << "Using the buffers " << ptr[rand() % FILE_LEN] << " " << buf[rand() % BUF_LEN] << " " << x << std::endl;
getrusage(RUSAGE_THREAD, &usage1);
std::cout << "========================" << std::endl;
std::cout << "Minor page faults = " << usage1.ru_minflt - usage0.ru_minflt << std::endl;
std::cout << "========================" << std::endl;
return 0;
}
./bench "/dev/shm/test.txt"
在 /dev/shm/ 使用 tmpfs 文件系统的地方运行,基准测试始终显示 0 个次要页面错误。运行./bench "/home/username/test.txt"
时,上面的基准测试显示了大约 200 个次要页面错误。即我用上面的命令看到这样的输出:
========================
Minor page faults = 231
========================
请注意,增加基准测试中的迭代次数也会增加次要页面错误的数量(例如,将迭代次数从 1000 更改为 2000 会导致大约 450 个次要页面错误)。
解决方案
推荐阅读
- spring-boot - 如何从用户名中获取访问令牌?
- swift - 停止 UItableView 从其单元格滚动
- java - 通过套接字连接:有时连接,有时不连接
- javascript - 在 Wordpress 中从一个 id 元素自动滚动到另一个 id 元素
- regex - if 条件中的表达式对于错误的模式匹配返回 true(如果我没有错)
- android - 防止在 Expo React Native 上进行屏幕录制
- php - 在 laravel 5.4 中无法发送没有发件人地址的消息
- octave - 导出的 PDF/PNG 中的字符消失
- macos - 如何将文件附加到苹果邮件撰写窗口?
- php - 如何在 Laravel 中的数字字符串列上运行 where 子句?