我有以下简单的程序,它基本上只是mmap
sa 文件并将其中的每个字节求和:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
volatile uint64_t sink;
int main(int argc, char** argv) {
if (argc < 3) {
puts("Usage: mmap_test FILE populate|nopopulate");
return EXIT_FAILURE;
}
const char *filename = argv[1];
int populate = !strcmp(argv[2], "populate");
uint8_t *memblock;
int fd;
struct stat sb;
fd = open(filename, O_RDONLY);
fstat(fd, &sb);
uint64_t size = sb.st_size;
memblock = mmap(NULL, size, PROT_READ, MAP_SHARED | (populate ? MAP_POPULATE : 0), fd, 0);
if (memblock == MAP_FAILED) {
perror("mmap failed");
return EXIT_FAILURE;
}
//printf("Opened %s of size %lu bytes\n", filename, size);
uint64_t i;
uint8_t result = 0;
for (i = 0; i < size; i++) {
result += memblock[i];
}
sink = result;
puts("Press enter to exit...");
getchar();
return EXIT_SUCCESS;
}
我是这样的:
gcc -O2 -std=gnu99 mmap_test.c -o mmap_test
你传递一个文件名和一个populate
或nopopulate
1,它控制是否MAP_POPULATE
传递给mmap
它。它等待您在退出之前输入 enter(让您有时间检查其中的内容/proc/<pid>
或其他内容)。
我使用 1GB 的随机数据测试文件,但你真的可以使用任何东西:
dd bs=1MB count=1000 if=/dev/urandom of=/dev/shm/rand1g
使用时MAP_POPULATE
,我希望页面缓存中的文件出现零主要错误和少量页面错误。我得到perf stat
了预期的结果:
perf stat -e major-faults,minor-faults ./mmap_test /dev/shm/rand1g populate
Press enter to exit...
Performance counter stats for './mmap_test /dev/shm/rand1g populate':
0 major-faults
45 minor-faults
1.323418217 seconds time elapsed
45 个错误仅来自运行时和进程开销(并且不依赖于映射文件的大小)。
但是,/usr/bin/time
报告了大约 15,300 个小故障:
/usr/bin/time ./mmap_test /dev/shm/rand1g populate
Press enter to exit...
0.05user 0.05system 0:00.54elapsed 20%CPU (0avgtext+0avgdata 977744maxresident)k
0inputs+0outputs (0major+15318minor)pagefaults 0swaps
top
和通过检查报告相同的约 15,300 个小故障/proc/<pid>/stat
。
现在,如果您不使用MAP_POPULATE
,所有方法,包括perf stat
同意,都会有大约 15,300 个页面错误。对于它的价值,这个数字来自1,000,000,000 / 4096 / 16 = ~15,250
- 即 1GB 分为 4K 页面,另外 16 倍的减少来自内核功能(“faultaround”),它在多达 15 个附近已经存在的页面中出现故障发生故障时的页面缓存。
谁在这里?根据 的记录行为MAP_POPULATE
,返回的数字perf stat
是正确的 - 单个mmap
调用已经填充了整个映射的页表,因此在触摸它时应该没有更多的小错误。
1实际上,除了 populate
作为nopopulate
.