1、简洁
Page Cache 存在于内核,目的是为了减少 I/O,提升应用的 I/O 速度。
2、速度对比
生成一个 200 MB 的文件用于测试:
~/tmp$ dd if=/dev/urandom of=~/tmp/200.bin bs=1M count=200
因为有缓存,可以看到速度很快:
~/tmp$ dd if=200.bin of=/dev/null bs=1M count=200
200+0 records in
200+0 records out
209715200 bytes (210 MB, 200 MiB) copied, 0.0363743 s, 5.8 GB/s
清理缓存之后,速度比较慢:
~/tmp$ sync && sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"
~/tmp$ dd if=200.bin of=/dev/null bs=1M count=200
200+0 records in
200+0 records out
209715200 bytes (210 MB, 200 MiB) copied, 0.506025 s, 414 MB/s
一个是 5.8 GB/s
,一个是 414 MB/s
,对比十分明显。
3、vmtouch
sudo apt install vmtouch
源码在 https://github.com/hoytech/vmtouch
用 vmtouch
可以查询 文件或目录是否有缓存,开始是100%
,清理缓存之后是0%
:
~/tmp$ vmtouch 200.bin
Files: 1
Directories: 0
Resident Pages: 51200/51200 200M/200M 100%
Elapsed: 0.003029 seconds
~/tmp$ sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"
~/tmp$ vmtouch 200.bin
Files: 1
Directories: 0
Resident Pages: 0/51200 0/200M 0%
Elapsed: 0.002153 seconds
~/tmp$
-e
选项可以仅清除指定文件的缓存:
~/tmp$ vmtouch 200.bin
Files: 1
Directories: 0
Resident Pages: 51200/51200 200M/200M 100%
Elapsed: 0.004441 seconds
~/tmp$ vmtouch -e 200.bin
Files: 1
Directories: 0
Evicted Pages: 51200 (200M)
Elapsed: 0.01934 seconds
~/tmp$ vmtouch 200.bin
Files: 1
Directories: 0
Resident Pages: 0/51200 0/200M 0%
Elapsed: 0.001222 seconds
4、vmtouch 的实现原理
下面写一个测试程序,主要由 mmap
和 mincore
两个系统调用实现,mincore
描述文件的哪些页在内核中,最低有效位表示页面是否驻留在内核中:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
unsigned long get_file_size(const char *path)
{
unsigned long filesize = -1;
struct stat statbuff;
if(stat(path, &statbuff) < 0)
{
return filesize;
}
else
{
filesize = statbuff.st_size;
}
return filesize;
}
void vmtouch_file(char *path)
{
int fd = open(path, O_RDONLY, 0);
if (fd == -1)
{
fprintf(stderr, "open(%s): %s\n", path, strerror(errno));
return;
}
long PAGE_SIZE = sysconf(_SC_PAGESIZE); // 4096
int64_t file_bytes = get_file_size(path);
int64_t file_pages = (file_bytes + PAGE_SIZE -1) / PAGE_SIZE;
char *mincore_array = malloc(file_pages);
if (mincore_array == NULL)
{
fprintf(stderr, "malloc: FATAL: %s\n", strerror(errno));
goto fail;
}
void *mem = mmap(NULL, file_bytes, PROT_READ, MAP_SHARED, fd, 0);
if (mem == MAP_FAILED)
{
fprintf(stderr, "mmap(%s): %s\n", path, strerror(errno));
goto fail;
}
if (mincore(mem, file_bytes, (void*)mincore_array))
{
fprintf(stderr, "mincore: FATAL: %s\n", strerror(errno));
goto fail;
}
int i;
int64_t total_pages_in_core = 0;
for (i = 0; i< file_pages; i++)
{
if ( mincore_array[i] & 0x1 )
{
total_pages_in_core++;
}
}
printf("Pages: %ld/%ld ", total_pages_in_core, file_pages);
printf("%.3g%% \n", 100.0*total_pages_in_core/file_pages);
fail:
if (mincore_array)
{
free(mincore_array);
}
if (mem)
{
if (munmap(mem, file_bytes))
{
fprintf(stderr, "munmap: %s\n", strerror(errno));
}
}
if (fd != -1)
{
close(fd);
}
}
int main(int argc, char* argv[])
{
if (argc != 2)
return -1;
vmtouch_file(argv[1]);
return 0;
}
测试效果,清缓存之前是100%
,清缓存之后是0%
:
~/tmp$ vmtouch 200.bin
Files: 1
Directories: 0
Resident Pages: 51200/51200 200M/200M 100%
Elapsed: 0.004504 seconds
~/tmp$ ./a.out 200.bin
Pages: 51200/51200 100%
~/tmp$ sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"
~/tmp$ ./a.out 200.bin
Pages: 0/51200 0%
~/tmp$ vmtouch 200.bin
Files: 1
Directories: 0
Resident Pages: 0/51200 0/200M 0%
Elapsed: 0.002726 seconds
网友评论