メモリ上にキャッシュされてるかどうか調べるやつ
これまじ面白いなー
ファイルがページキャッシュに乗っているかどうかを調べる - ablog
指定したファイルがメモリ上でキャッシュされてるか調べられるというもの。しかし、どうやってんだろってことでソースを読んでみた。
結論から言うとmmapとmincoreで調べられるんですね。
まずはファイルを開いてファイルディスクリプタを取得。そしてファイルの情報をfstatで取得。
ここまでは普通だ。ただこのプログラムで使っているのはst.st_size (ファイルサイズのバイト数) だけっぽい。
fd = open(filename, 0); if (0 != fstat(fd, &st)) { ... // 失敗した時の処理 }
次にmmapでコマンドラインで指定されたファイルをメモリ上にマップする
pa = mmap((void *)0, st.st_size, PROT_NONE, MAP_SHARED, fd, 0);
まぁ普通にファイルのデータを全部メモリ上に読み込ませている。そのときPROT_NONEを指定しているので作成したページにはアクセスできない、このプログラムは読み込んだデータにアクセスする必要がないからPROT_NONEを指定している感じかな。データ使わねぇよみたいな。MAP_SHAREDを指定しているので、他のプロセスとこのマッピングを共有する。ということになる。
最初の引数は(void *)0となっているがおそらくこれは本当はNULLを指定するべき? ただ(void *)0がNULLとしてC言語では定義されていた気もするので同義っぽい。いずれにせよこれがNULLであれば、カーネルがマッピングするアドレスを決定するらしい。つまり、既にそのファイルがメモリ上でキャッシュされてた場合はカーネルがココでよしなにそれを利用するようにしてくれるってことだろうなと予想。
size_t pageSize = getpagesize();
getpagesize関数
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/getpagesize.2.html
getpagesize() 関数はメモリページの大きさをバイト数で返す。 ここでいう「ページ」は固定長のブロックであり、 mmap(2) で実行されるメモリ割り当てとファイルマッピングの単位である。
ページサイズというのが利用可能なメモリの領域の1区画当たりのバイト数であるという認識で読み進めてみる。この関数でなんらかの1区画あたりのバイト数を取得すると。(というかなんかmanに、「移植性を考えるならsysconf(_SC_PAGESIZE)を利用するべきである」って書いてあるな。)
次に
printf("page num: %ld\n", (st.st_size+pageSize-1)/pageSize);
このコードでページ数を求めてるんだろうな、というのは文脈から分かるんだけど、st.st_size / pageSize ではないのはなぜ。ああ、常に1ページ多めになるようにしてるみたいな感じかな。(うまく説明できない) まぁとにかくページ数がこれで求められるんだなという前提で読み進める
vec = calloc(1, (st.st_size+pageSize-1)/pageSize); if (0 != mincore(pa, st.st_size, vec)) {
このコードで先ほど算出したページ数のぶんだけメモリを割り当てている。
この場合たとえば4ページだったら4バイト、かな。そんでそれをmincore関数に渡している。
mincore関数
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man2/mincore.2.html
mincore() は、呼び出し元プロセスの仮想メモリのページがコア (RAM) 内に存在し、 ページ参照時にディスクアクセス (ページ・フォールト) を起こさないか どうかを示すベクトルを返す。カーネルは、アドレス addr から始まる length バイトの範囲のページに関する存在情報を返す。
vec 引き数は、少なくとも (length+PAGE_SIZE-1) / PAGE_SIZE バイトが格納できる配列を指していなければならない。 この呼び出しが返るとき、 各バイトの最下位ビットは、対応するページがメモリ内にそのとき存在すれば セットされ、そうでない場合はクリアされる (各バイトのその他のビットは未定義であり、これらのビットは将来の使用に そなえて予約されている)。
とのことらしく、簡単に言えばページ数分だけ用意されたvecって配列にそれぞれのページの情報が入っていて、最下位バイトが1のときはディスクアクセスが不要 = メモリ上にキャッシュされてるってことが判別できるよってことなんですね。このプログラムのコアはmincore関数だったのかー。
for (pageIndex = 0; pageIndex <= st.st_size/pageSize; pageIndex++) { if (vec[pageIndex]&1) { //printf("%lu\n", (unsigned long)pageIndex); sum++; } }
vec[pageIndex] に入ってる値は、
各バイトの最下位ビットは、対応するページがメモリ内にそのとき存在すれば セットされ、
とのことなので、vec[pageIndex] & 1をしたときにそれが1であるときはメモリにキャッシュされていると。そしてC言語は0がfalseで、0以外がtrueなので
if( vec[pageIndex] & 1 ){ sum++; }
とすることでメモリ上のどこかのページにキャッシュされてるときにsum変数がインクリメントされると。