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 #include3 #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_______________________
2024 年 09 月 02 日 Vigiles サポート
2024 年 03 月 01 日 Vigiles サポート
2023 年 08 月 28 日 Vigiles サポート
2024 年 03 月 26 日 Yocto Project よもやま話
2023 年 07 月 25 日 Yocto Project よもやま話
2023 年 06 月 20 日 Yocto Project よもやま話
2024 年 01 月 10 日 Linux 技術ネタ
2023 年 12 月 12 日 Linux 技術ネタ
2023 年 03 月 31 日 Linux 技術ネタ
2024 年 07 月 26 日 イベントレポート
2024 年 07 月 09 日 イベントレポート
2024 年 06 月 03 日 イベントレポート
2023 年 05 月 30 日 リクルート
2022 年 12 月 27 日 リクルート
2022 年 09 月 27 日 リクルート
2024 年 09 月 25 日 信州リネオ便り
2024 年 08 月 20 日 信州リネオ便り
2024 年 08 月 07 日 信州リネオ便り
2019 年 12 月 10 日 ソリューション統括部
2019 年 12 月 10 日 ソリューション統括部
2019 年 12 月 10 日 ソリューション統括部
2019 年 12 月 13 日 マーケティング統括部
2019 年 04 月 25 日 マーケティング統括部
2018 年 12 月 18 日 マーケティング統括部