*

page-typesについて

   

linuxのカーネルソースツリーにはカーネル本体のコードの他に開発者に有益なドキュメントやツール、スクリプト類が含まれています。
今回はtools/vmディレクトリにあるpage-types.cを紹介します。

構築方法

page-typesはカーネルソースツリーのトップディレクトリで下記のコマンドを実行すると構築することができます。
--------------------------------------------------------------------------------
$ make -C tools/vm
--------------------------------------------------------------------------------

使い方

page-typesを単純に実行すると下記のような結果が出力されます。(page-typesは内部で/proc/kpagemapsにアクセスしてカーネルから情報を収集するため、sudoを付けて実行する必要があります)これはシステム全体で使用しているページフレームをフラグの種類(組み合わせ)別に集計した結果です。下記のsymbolic-flags列はページフレームを管理するpage構造体のflagsビットフィールドの内容を表していて、この情報からページフレームの状態や何に使われているかを推測することができます。——————————————————————————–

$ cd tools/vm
$ sudo ./page-types
             flags      page-count       MB  symbolic-flags                     long-symbolic-flags
0x0000000000000000          424756     1659  ___________________________________
0x0000000000400000           14819       57  ______________________t____________        thp
0x0000000000100000          262144     1024  ____________________n______________        nopage
0x0000000000000014               5        0  __R_D______________________________        referenced,dirty
0x0000000000000028          256277     1001  ___U_l_____________________________        uptodate,lru
0x0001000000000028             911        3  ___U_l_________________________I___        uptodate,lru,readahead
0x000000000000002c          201593      787  __RU_l_____________________________        referenced,uptodate,lru
0x0000000000004030            1623        6  ____Dl________b____________________        dirty,lru,swapbacked
0x000000000000403c              30        0  __RUDl________b____________________        referenced,uptodate,dirty,lru,swapbacked
0x0000000000000068           10176       39  ___U_lA____________________________        uptodate,lru,active
0x000000000000006c           50359      196  __RU_lA____________________________        referenced,uptodate,lru,active
0x0000000000004078              13        0  ___UDlA_______b____________________        uptodate,dirty,lru,active,swapbacked
0x000000000000407c             162        0  __RUDlA_______b____________________        referenced,uptodate,dirty,lru,active,swapbacked
0x000000000000007c               3        0  __RUDlA____________________________        referenced,uptodate,dirty,lru,active
0x0000000000000080           21217       82  _______S___________________________        slab
0x0000000000000400            2252        8  __________B________________________        buddy
0x0000000000000800               2        0  ___________M_______________________        mmap
...
--------------------------------------------------------------------------------
また表示されている各フラグの名称は下記のようにヘルプで参照できます。これらフラグはカーネルソース上ではPG_*というenum型の
値として定義されています。例えばSフラグ(PG_slab)が設定されたページは現在スラブとして消費されています。スラブは主にカー
ネルの内部で使用する各種オブジェクトのインスタンスを作るためのメモリアロケータです。ファイルシステムで使うディレクトリエン
トリやinode、プロセス管理で使うタスク構造体(task_struct)などもスラブから生成されます。なおスラブの詳しい使用状況は
/proc/slabinfoを参照することで確認することができます。
--------------------------------------------------------------------------------
$ ./page-type -h
...
bit-names:
          locked              error         referenced           uptodate
           dirty                lru             active               slab
       writeback            reclaim              buddy               mmap
       anonymous          swapcache         swapbacked      compound_head
   compound_tail               huge        unevictable           hwpoison
          nopage                ksm                thp           reserved(r)
         mlocked(r)    mappedtodisk(r)         private(r)       private_2(r)
   owner_private(r)            arch(r)        uncached(r)       readahead(o)
       slob_free(o)     slub_frozen(o)      slub_debug(o)
                                   (r) raw mode bits  (o) overloaded bits
--------------------------------------------------------------------------------

page-typesを使って特定のプロセスの集計情報だけを取り出したい場合は下記のように-pオプションを付けて実行します。
--------------------------------------------------------------------------------
$ sudo ./page-types -p 6088    # PID:6088のプロセスの集計情報を表示
             flags      page-count       MB  symbolic-flags                     long-symbolic-flags
0x0000000000000800               1        0  ___________M_______________________        mmap
0x0000000000000828               4        0  ___U_l_____M_______________________        uptodate,lru,mmap
0x000000000000082c               2        0  __RU_l_____M_______________________        referenced,uptodate,lru,mmap
0x0000000000000868              17        0  ___U_lA____M_______________________        uptodate,lru,active,mmap
0x000000000000086c             437        1  __RU_lA____M_______________________        referenced,uptodate,lru,active,mmap
0x0000000000005868             423        1  ___U_lA____Ma_b____________________        uptodate,lru,active,mmap,anonymous,swapbacked
0x000000000000586c               1        0  __RU_lA____Ma_b____________________        referenced,uptodate,lru,active,mmap,anonymous,swapbacked
             total             885        3
--------------------------------------------------------------------------------

また集計情報ではなくプロセス空間にマッピングされている個々のページフレームの情報を参照したい場合、下記のように-lまたは
-Lオプションを付けて実行します。(-Nは集計情報を省略するオプションです)
--------------------------------------------------------------------------------

$ sudo ./page-types -p 6088 -l -N
voffset offset  len     flags
400     13f64f  1       __RU_lA____M_______________________
401     13f64e  1       __RU_lA____M_______________________
402     13f64d  1       __RU_lA____M_______________________
403     13f64c  1       __RU_lA____M_______________________
404     13fbb9  2       __RU_lA____M_______________________
407     13fb82  1       __RU_lA____M_______________________
...
4c0     bbd6a   1       __RU_lA____M_______________________
4c1     357b8   3       __RU_lA____M_______________________
6dc     8680e   1       ___U_lA____Ma_b____________________
6dd     af25c   1       ___U_lA____Ma_b____________________
6de     8de6a   1       ___U_lA____Ma_b____________________
6df     852de   1       ___U_lA____Ma_b____________________
...
7f2e0c219       9e1e7   1       ___U_lA____Ma_b____________________
7ffffedc1       ab7dd   1       ___U_lA____Ma_b____________________
7ffffedc2       ab5cc   1       ___U_lA____Ma_b____________________
7ffffedc3       8f7f4   1       ___U_lA____Ma_b____________________
7ffffedc4       8599d   1       ___U_lA____Ma_b____________________
7ffffedc5       8d0a0   1       ___U_lA____Ma_b____________________
7ffffedc6       8f19f   1       ___U_lA____Ma_b____________________
7ffffedc7       b1411   1       ___U_lA____Ma_b____________________
7ffffedc8       a94ac   1       __RU_lA____Ma_b____________________
7ffffede9       1941    1       ___________M_______________________
--------------------------------------------------------------------------------
voffset列、offset列はそれぞれプロセス空間のページフレーム単位のオフセット値とPFN(ページ・フレーム・ナンバー)で、
PFNは通常物理アドレスをページサイズで割った値となっています。つまりこれはプロセスの仮想アドレスと物理アドレスの
対応関係を表しています。またlen列は連続して割り当てられているページフレーム数を表しています。-lオプションの代わり
に-Lを指定した場合は、連続したページをまとめず1ページずつ表示します。(len列は省略されます)具体例
ここでは例として、page-typesを使ってプロセス空間に実際にページが割り当てられるタイミングを確認したいと思います。
まず確認のために下記のようなテストプログラムを用意します。このプログラムは引数として2つの数字をとります。
ひとつ目はmallocで確保する領域のサイズ[MB]で、2つ目はその確保した領域に値(0xf)を書き込むサイズ[MB]になります。
実行するとmallocした後で一旦止まり、一度enterキーを押すとmallocした領域への書き込みを行います。
そしてもう一度enterキーを押すと終了します。
--------------------------------------------------------------------------------
  1 /* test.c */
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 #include <string.h>
  5
  6 void print_usage(void)
  7 {
  8         printf("usage: memeater ALLOC_SIZE EAT_SIZE\n");
  9         printf("  ALLOC_SIZE: mallocで確保する領域のサイズ[MB]\n");
 10         printf("  EAT_SIZE: 確保した領域中で実際にページをマップするサイズ[MB]\n");
 11
 12         return;
 13 }
 14
 15 int main(int argc, char *argv[])
 16 {
 17         int asize, esize;
 18         void *addr;
 19
 20         if (argc != 3 ) {
 21                 printf("error: 引数の指定が必要です。\n");
 22                 print_usage();
 23                 return -1;
 24         }
 25
 26         asize = atoi(argv[1]);
 27         if (asize < 0) {
 28                 printf("error: ALLOC_SIZEの値が正しくありません。\n");
 29                 print_usage();
 30                 return -1;
 31         } else {
 32                 asize = asize * 1024 * 1024;
 33         }
 34
 35         esize = atoi(argv[2]);
 36         if (esize < 0) {
 37                 printf("error: EAT_SIZEの値が正しくありません。\n");
 38                 print_usage();
 39                 return -1;
 40         } else {
 41                 esize = esize * 1024 * 1024;
 42         }
 43
 44         addr = malloc(asize);
 45         if (addr > 0) {
 46                 printf("malloc成功!続けるにはenterキーを押してください。\n");
 47                 getchar();
 48         } else {
 49                 printf("malloc失敗!\n");
 50                 return -1;
 51         }
 52
 53         memset(addr, 0xf, esize);
 54         printf("終了するにはenterキーを押してください。\n");
 55         getchar();
 56
 57         free(addr);
 58         return 0;
 59 }
--------------------------------------------------------------------------------

まずテストプログラムを実行した直後(上記47行目時点)のプロセス空間のマップ状況を確認します。なお今回mallocで確保するサイズは500MB、
値を書き込むサイズは50MBしました。
/proc/$PID/mapsで確認すると、サイズから「7f777a87f000-7f7799c80000」の領域がmallocされた領域と考えられます。
--------------------------------------------------------------------------------
$ ./test 500 50
malloc成功!続けるにはenterキーを押してください。

# 別の端末で/proc/$PID/mapsを確認します。なおPIDはpsコマンドで確認しておきます(今回は11900)。
$ cat /proc/11900/maps
00400000-00401000 r-xp 00000000 fd:01 102393658                          /home/lineo/work/test/test
00600000-00601000 r--p 00000000 fd:01 102393658                          /home/lineo/work/test/test
00601000-00602000 rw-p 00001000 fd:01 102393658                          /home/lineo/work/test/test
7f777a87f000-7f7799c80000 rw-p 00000000 00:00 0  <-- mallocで確保した領域
7f7799c80000-7f7799e36000 r-xp 00000000 fd:01 33557670                   /usr/lib64/libc-2.17.so
...
7ffe5dca9000-7ffe5dcca000 rw-p 00000000 00:00 0                          [stack]
7ffe5dd78000-7ffe5dd7a000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
--------------------------------------------------------------------------------

次にpage-typesで同プロセスの情報を表示してみます。
するとmallocで確保した領域「7f777a87f000-7f7799c80000」にはまだ1ページしか物理ページが割り当てられていないことが分かります。
--------------------------------------------------------------------------------
$ sudo ./page-types -p 11900 -l | less
voffset offset  len     flags
400     9adee   1       __RU_lA____M_______________________
600     9aaae   1       ___U_lA____Ma_b____________________
601     9adcb   1       ___U_lA____Ma_b____________________
7f777a87f       a7e10   1       ___U_lA____Ma_b____________________  <-- マップされたページ
7f7799c80       bbec3   1       __RU_lA____M_______________________
7f7799c81       bbcd0   2       __RU_lA____M_______________________
7f7799c83       bbce0   1       __RU_lA____M_______________________
7f7799c84       bbc9b   1       __RU_lA____M_______________________
7f7799c85       bbce4   1       __RU_lA____M_______________________
7f7799c86       1b71    1       __RU_lA____M_______________________
7f7799c87       bbc78   2       __RU_lA____M_______________________

--------------------------------------------------------------------------------

ではテストプログラムに戻ってenterキーを押してみます。これでmallocされた領域の先頭50MB分の領域に値が書き込まれました。
--------------------------------------------------------------------------------
$ ./test 500 50
malloc成功!続けるにはenterキーを押してください。
# enterキー押下
終了するにはenterキーを押してください。
--------------------------------------------------------------------------------

再びpage-typesで確認してみます。今度は下記のようにプロセス空間の0x7f777a87f000から0x7f777da01000まで物理ページがマップされています。
0x7f777da01000 - 0x7f777a87f000 = 0x3182000 = 約50MB となり、値を書き込んだ領域のみページが割り当てられていることが確認できました。
これがLinuxのメモリ管理方式の一つである「デマンドページング」と呼ばれる仕組みで、カーネルはプログラムが実際にアクセスするまで物理ページの
割り当てを遅延させます。この仕組みによって限られたメモリを効率的に利用することができるようになっています。
--------------------------------------------------------------------------------
$ sudo ./page-types -p 11900 -l | less
voffset offset  len     flags
400     9adee   1       __RU_lA____M_______________________
600     9aaae   1       ___U_lA____Ma_b____________________
601     9adcb   1       ___U_lA____Ma_b____________________
7f777a87f       a7e10   1       ___U_lA____Ma_b____________________  <-- ここから
7f777a880       ab88a   1       ___U_lA____Ma_b____________________
7f777a881       959e1   1       ___U_lA____Ma_b____________________
7f777a882       abaef   1       ___U_lA____Ma_b____________________
7f777a883       959e4   1       ___U_lA____Ma_b____________________
7f777a884       b639c   1       ___U_lA____Ma_b____________________
7f777a885       b0758   1       ___U_lA____Ma_b____________________
7f777a886       aca1f   1       ___U_lA____Ma_b____________________
7f777a887       9a128   1       ___U_lA____Ma_b____________________
7f777a888       959d4   1       ___U_lA____Ma_b____________________
…
7f777d800       90200   1       ___U_lA____Ma_b_______t____________
7f777d801       90201   1ff     ______________________t____________
7f777da00       125200  1       ___U_lA____Ma_b_______t____________
7f777da01       125201  1ff     ______________________t____________  <-- ここまで
7f7799c80       bbec3   1       __RU_lA____M_______________________
7f7799c81       bbcd0   2       __RU_lA____M_______________________
7f7799c83       bbce0   1       __RU_lA____M_______________________
7f7799c84       bbc9b   1       __RU_lA____M_______________________
--------------------------------------------------------------------------------

 - 未分類