Dive into linux-2.6/mm
@syuu1228 せんせーのお誕生日です!
この記事は [twitter:@syuu1228] せんせーのお誕生日祝いに書かれました(多分)
ここから
まぁとりあえずコードをあたりますよね!
ということで、あたってみた結果です。情報が正しいかどうかはわかりません。
そもそもこの「Reserved」ってなに?
linux-2.6/arch/x86/mm/init_64.c を見てみましょう。
void __init mem_init(void) { long codesize, reservedpages, datasize, initsize; ... reservedpages = 0; ... /* this will put all low memory onto the freelists */ #ifdef CONFIG_NUMA totalram_pages = numa_free_all_bootmem(); #else totalram_pages = free_all_bootmem(); #endif absent_pages = absent_pages_in_range(0, max_pfn); reservedpages = max_pfn - totalram_pages - absent_pages; ... printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, " "%ldk absent, %ldk reserved, %ldk data, %ldk init)\n", nr_free_pages() << (PAGE_SHIFT-10), max_pfn << (PAGE_SHIFT-10), codesize >> 10, absent_pages << (PAGE_SHIFT-10), reservedpages << (PAGE_SHIFT-10), datasize >> 10, initsize >> 10); }
numa_free_all_bootmem() は linux-2.6/arch/x86/mm/numa_64.c から。まぁ、 free_all_bootmem() と基本的には変わらないはずで "release free pages to the buddy allocator" して、解放されたページの数が返ってきます。
ということで、 "reserved" はメモリ全体から「ブート時に使ったメモリ」と「存在してないメモリ」をぬいた分の様子であんまり関係はなさそう。
じゃあ、もともとの nr_free_pages() はどうなっているんだろうか?
linux-2.6/include/linux/swap.h
/* Definition of global_page_state not available yet */ #define nr_free_pages() global_page_state(NR_FREE_PAGES)
から
linux-2.6/include/linux/vmstat.h
static inline unsigned long global_page_state(enum zone_stat_item item) { long x = atomic_long_read(&vm_stat[item]); #ifdef CONFIG_SMP if (x < 0) x = 0; #endif return x; }
ようするに、 "vm_stat[NR_FREE_PAGES]" になる様子。
さて、この vm_stat[NR_FREE_PAGES] はどう計算するのでしょう?
linux-2.6/mm/bootmem.c から
static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) { ... while (start < end) { unsigned long *map, idx, vec; map = bdata->node_bootmem_map; idx = start - bdata->node_min_pfn; vec = ~map[idx / BITS_PER_LONG]; if (aligned && vec == ~0UL && start + BITS_PER_LONG < end) { int order = ilog2(BITS_PER_LONG); __free_pages_bootmem(pfn_to_page(start), order); count += BITS_PER_LONG; } else { unsigned long off = 0; while (vec && off < BITS_PER_LONG) { if (vec & 1) { page = pfn_to_page(start + off); __free_pages_bootmem(page, 0); count++; } vec >>= 1; off++; } } start += BITS_PER_LONG; } page = virt_to_page(bdata->node_bootmem_map); pages = bdata->node_low_pfn - bdata->node_min_pfn; pages = bootmem_bootmap_pages(pages); count += pages; while (pages--) __free_pages_bootmem(page++, 0); ... }
3個所で __free_pages_bootmem が呼ばれてますね。この関数 (linux-2.6/mm/page_alloc.c) が __free_page() を呼びにいきます。 んで、ごにょごにょとして free_one_page() が呼ばれてこれが "__mod_zone_page_state(zone, NR_FREE_PAGES, 1 << order);" で "1 << order" だけ vm_stat[NR_FREE_PAGES] を増やします。
…というわけで、 bootmem が確保していた分が計上されているようなのです。bootmem は boot 時に使われる簡易的なメモリアロケータですので、そうすると、 bootmem が使える、と思っている領域がそもそも少ない、ということだと思います。
んで、 bootmem は最初「全てを予約済み(=使えない)」にしておいて「全てをフリー(=使える)」にして、再度カーネルやらbootmem管理テーブル領域を「予約」します。
gist のを見る限り、 zone の中のページ数認識は間違えていないようなのでこの「予約」の部分になにかあるのではないでしょうか………
…というところで、お腹もすいてきたし借りてきた指輪物語見たいし dmesg がびみょうに予想にあわなかったり bootmem_debug=1 がついてたらなあと思ってきたので離脱します><
では、最後に
@@@@ @ Happy @ Λ@Birthday@ (・∀・@@@@ ヽ っ\ / ∪∪ /∞ヽ