リネオブログ

page-types について

2016 年 04 月 28 日   Linux 技術ネタ

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 
  3 #include 
  4 #include 
  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_______________________
Yocto Project 公式実践講座 7 月 ~ 9 月 開催分 受講お申込み受付中!
Yocto Project よもやま話
Yocto よもやま話 第 2 回「Yocto Project SUMMIT 2022/05」
Yocto よもやま話 第 2 回「Yocto Project SUMMIT 2022/05」

2022 年 06 月 20 日 Yocto Project よもやま話

新企画「Yocto Project よもやま話」の連載を始めます
新企画「Yocto Project よもやま話」の連載を始めます

2022 年 04 月 28 日 Yocto Project よもやま話

Linux 技術ネタ
イベントレポート
ET & IoT 2021 レポート
ET & IoT 2021 レポート

2021 年 12 月 01 日 イベントレポート

組込み総合技術展 Embedded Technology 2019 レポート
組込み総合技術展 Embedded Technology 2019 レポート

2019 年 12 月 13 日 イベントレポート

組込み総合技術展 関西 ETWest2019 レポート
組込み総合技術展 関西 ETWest2019 レポート

2019 年 06 月 20 日 イベントレポート

リクルート
新卒採用、絶賛募集中!
新卒採用、絶賛募集中!

2022 年 06 月 06 日 リクルート

違うズラ
違うズラ

2018 年 10 月 26 日 リクルート

信州で人生がかわる
信州で人生がかわる

2018 年 07 月 24 日 リクルート

北小野通信
入笠山ハイキング その 2
入笠山ハイキング その 2

2019 年 10 月 09 日 北小野通信

入笠山ハイキング その 1
入笠山ハイキング その 1

2019 年 10 月 09 日 北小野通信

氷の世界
氷の世界

2019 年 01 月 22 日 北小野通信

ソリューション統括部
シリコンバレー探検記 2019 ~番外編~
シリコンバレー探検記 2019 ~番外編~

2019 年 12 月 10 日 ソリューション統括部

シリコンバレー探検記 2019 ~後編~
シリコンバレー探検記 2019 ~後編~

2019 年 12 月 10 日 ソリューション統括部

シリコンバレー探検記 2019 ~前編~
シリコンバレー探検記 2019 ~前編~

2019 年 12 月 10 日 ソリューション統括部

マーケティング統括部
大成功決起大会!!(ET2019)
大成功決起大会!!(ET2019)

2019 年 12 月 13 日 マーケティング統括部

ESEC 2019 決起大会
ESEC 2019 決起大会

2019 年 04 月 25 日 マーケティング統括部

シリコンバレー探検記 その 2
シリコンバレー探検記 その 2

2018 年 12 月 18 日 マーケティング統括部