ablog

不器用で落着きのない技術者のメモ

ファイルがページキャッシュに乗っているかどうかを調べる

Linux上で任意のファイルがページキャッシュに乗っているかどうか調べるCで書かれたプログラムを見つけたので、コンパイルして実行してみた。

Linux上のとあるファイルがページキャッシュに乗っているかどうかを調べたいなーと思ってGoogle先生にご相談したところ、こんなコマンドを教えてくれた。

ファイルをメモリにマップして、mincore(2)でページごとにRAMに存在するかどうかをチェックしているらしい。


mmapしても即メモリにロードされるわけではないのかぁ。


Cの部分だけ抜き出して、単体で動かしてみた。

#include <errno.h> /* errno */
#include <fcntl.h> /* fcntl, open */
#include <stdio.h> /* perror, fprintf, stderr, printf */
#include <stdlib.h> /* exit, calloc, free */
#include <string.h> /* strerror */
#include <sys/stat.h> /* stat, fstat */
#include <sys/types.h> /* size_t */
#include <unistd.h> /* sysconf, close */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

void
fincore(char *filename) {
   int fd;
   struct stat st;
   void *pa = (char *)0;
   char *vec = (char *)0;
   register size_t n = 0;
   size_t pageSize = getpagesize();
   register size_t pageIndex;
   int sum = 0;

   printf("pagesize: %ld\n", pageSize);

   fd = open(filename, 0);

   if (0 > fd) {
      perror("open");
      return;
   }

   if (0 != fstat(fd, &st)) {
      perror("fstat");
      close(fd);
      return;
   }

   printf("filesize: %ld\n", st.st_size);

   pa = mmap((void *)0, st.st_size, PROT_NONE, MAP_SHARED, fd, 0);
   if (MAP_FAILED == pa) {
      perror("mmap");
      close(fd);
      return;
   }

   printf("page num: %ld\n", (st.st_size+pageSize-1)/pageSize);

   /* vec = calloc(1, 1+st.st_size/pageSize); */
   vec = calloc(1, (st.st_size+pageSize-1)/pageSize);
   if ((void *)0 == vec) {
      perror("calloc");
      close(fd);
      return;
   }

   if (0 != mincore(pa, st.st_size, vec)) {
      /* perror("mincore"); */
      fprintf(stderr, "mincore(%p, %lu, %p): %s\n",
              pa, (unsigned long)st.st_size, vec, strerror(errno));
      free(vec);
      close(fd);
      return;
   }

   /* handle the results */
   for (pageIndex = 0; pageIndex <= st.st_size/pageSize; pageIndex++) {
      if (vec[pageIndex]&1) {
         //printf("%lu\n", (unsigned long)pageIndex);
         sum++;
      }
   }

   free(vec);
   vec = (char *)0;

   munmap(pa, st.st_size);
   close(fd);

   printf("in memory pages: %d\n", sum);

   return;
}

int main(int argc, char **argv) {
  fincore(argv[1]);
}
ファイルがキャッシュに乗ってのっているかどうかを調べる - lambda {|diary| lambda { diary.succ! } }.call(hatena)

とりあえず、コピペしてコンパイルして実行してみる。

コンパイルする

oel11gr2 root% gcc -o fincore_min fincore_min.c
oel11gr2 root% ls -l fincore_min*
-rwxr-xr-x 1 root root 6825 Nov 11 02:09 fincore_min
-rw-r--r-- 1 root root 1974 Nov 11 01:56 fincore_min.c

使ってみる

  • free コマンドで cached のサイズを確認しておく。
oel11gr2 root% free

             total       used       free     shared    buffers     cached
Mem:       1035064     225656     809408          0      15992     161140
-/+ buffers/cache:      48524     986540
Swap:      2096472          0    2096472
  • 今回、ページキャッシュに乗せるファイル「dummy」のサイズは100MB。
oel11gr2 root% ls -lh dummy
-rw-r--r-- 1 root root 100M Nov 11 02:16 dummy
  • ページキャッシュに乗っているページは0個。
oel11gr2 root% ./fincore_min dummy
pagesize: 4096
filesize: 104857600
page num: 25600
in memory pages: 0
  • ファイルを読み込んで、ページキャッシュに乗せる。
oel11gr2 root% cat dummy > /dev/null
  • 見事に全ページがページキャッシュに乗っている。
oel11gr2 root% ./fincore_min dummy
pagesize: 4096
filesize: 104857600
page num: 25600
in memory pages: 25600
  • free で cached を確認すると、100MB増えている。
oel11gr2 root% free
             total       used       free     shared    buffers     cached
Mem:       1035064     329804     705260          0      16140     263764
-/+ buffers/cache:      49900     985164
Swap:      2096472          0    2096472
  • キャッシュを解放する。
oel11gr2 root% echo 3 > /proc/sys/vm/drop_caches
  • ページキャッシュに乗っているページが 0 個になる。
oel11gr2 root% ./fincore_min dummy
pagesize: 4096
filesize: 104857600
page num: 25600
in memory pages: 0

参考

追記(2012/11/14):
Documentation/filesystems/proc.txt には以下の通り書かれていました。

drop_caches
-----------

Writing to this will cause the kernel to drop clean caches, dentries and
inodes from memory, causing that memory to become free.

To free pagecache:
	echo 1 > /proc/sys/vm/drop_caches
To free dentries and inodes:
	echo 2 > /proc/sys/vm/drop_caches
To free pagecache, dentries and inodes:
	echo 3 > /proc/sys/vm/drop_caches