トータルメモリ
システムの全メモリーはproc/meminfoのMemTotal:項目で表示されまが、一体この値はどのように取得されているのでしょうか?
bdata->node_min_pfnにはカーネルコードの終端ページフレームで、bdata->node_min_pfnにはハイメモリーでないメモリ空間のページフレーム数のようです。(たぶん)この間をbdata->node_bootmem_mapを元にバディーシステムに使用可能ページとして登録します。
aligned = !(start & (BITS_PER_LONG - 1))はスタートページフレームがワードサイズでアライメントされたいるかチェックします。これは続く32ページも利用可能ページだった場合、バディーシステムへの登録の際、6のオーダで登録するかどうかの判断のようです。(たぶん)
で、while (start < end)で、全ページフレームをチェックします。idx = start - bdata->node_min_pfnでチェックするページフレームの0オリジンとするインデックスで、vec = ~map[idx / BITS_PER_LONG]で対応する32ページフレームのビットとチェックすることで、そのインデックスに対する32個のページフレームが利用可能かどうかのチェックとなるわけです。(この辺りのビット処理での実装は凡人には思いつかないな。と感心させられます。)
そうなら、一挙にorder = ilog2(BITS_PER_LONG)のオーダで(たぶんBITS_PER_LONGが32として6?)、__free_pages_bootmem()でバディーシステムに登録です。
そでないなら、BITS_PER_LONG回、vec >>= 1でビットマップをチェックすることで、1ページづつ__free_pages_bootmem()でバディーシステムに登録しているようです。
whileループを抜けた後の処理は、bdata->node_bootmem_mapで使っていたページフレームは用済みとなったので、本ページを利用可能ページとしてバディーシステムに登録しています(実にきっちりしています。)page = virt_to_page(bdata->node_bootmem_map)はbdata->node_bootmem_mapが使用している先頭ページです。pages = bdata->node_low_pfn - bdata->node_min_pfnはチェックするページ数、すなわちbdata->node_bootmem_mapのサイズが決定し、従ってbdata->node_bootmem_mapで使用されていたページ数が算出しています。そしてそのページをすべてバディーシステムに登録しています。
バディーシステムに登録し全ページはcountに保持されています。すなわちこれがここでの全メモリ(ページ数)というわけです。
[kitamura@dhcppc4 ~]$ cat /proc/meminfo MemTotal: 473404 kB MemFree: 15140 kB Buffers: 51860 kB Cached: 350048 kB : :proc/meminfoを読み込むと、meminfo_read_proc()がコールされます。以下はその処理を端折ったものですが、MemTotal:はsi_meminfo()で取得した値を、K(i.totalram)として、そのまま表示しています。(KマクロはKバイトに変換するものです。)
static int meminfo_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
si_meminfo(&i);
si_swapinfo(&i);
len = sprintf(page,
"MemTotal: %8lu kB\n"
"MemFree: %8lu kB\n"
"Buffers: %8lu kB\n"
K(i.totalram),
K(i.freeram),
K(i.bufferram),
);
}
si_meminfo()関数ではval->totalram = totalram_pagesと、totalram_pagesをスタティック変数をそのまま設定しているに過ぎません。この変数が全メモリーの実態と言えそうですが、ではこの値とは?と言う事です。
void si_meminfo(struct sysinfo *val)
{
val->totalram = totalram_pages;
val->sharedram = 0;
val->freeram = global_page_state(NR_FREE_PAGES);
val->bufferram = nr_blockdev_pages();
val->totalhigh = totalhigh_pages;
val->freehigh = nr_free_highpages();
val->mem_unit = PAGE_SIZE;
}
totalram_pagesを設定する処理としては、start_kernel()から呼ばれるmem_init()およびrest_init()で行われているようです。mem_init()では通常メモリとハイメモリ間での処理で、rest_init()ではカーネル初期化コードで必要なくなったメモリ空間の処理です。
void start_kernel(void)
{
:
mem_init();
:
rest_init();
}
void __init mem_init(void)
{
:
totalram_pages += free_all_bootmem();
:
set_highmem_pages_init();
:
}
free_all_bootmem()関数はbootmem_data_t *bdataを引数としてfree_all_bootmem_core()をコールしています。bootmem_data_t *bdataにはBIOSから取得したメモリ情報をページフレーム単位で設定しているようですが、そうだとすると、なぜわざわざ本関数をコールするのか?と言うことです。bootmem_data_t *bdataに設定されているページフレームには、BIOSで使用しているとか、VRAM等で使用できないメモリ空間があります。その辺りを本関数で考慮しています。その情報はbdata->node_bootmem_mapにビットマップとして有していて、この情報を元に使用ページフレームをバディーシステムに登録することにあるようです。bdata->node_min_pfnにはカーネルコードの終端ページフレームで、bdata->node_min_pfnにはハイメモリーでないメモリ空間のページフレーム数のようです。(たぶん)この間をbdata->node_bootmem_mapを元にバディーシステムに使用可能ページとして登録します。
aligned = !(start & (BITS_PER_LONG - 1))はスタートページフレームがワードサイズでアライメントされたいるかチェックします。これは続く32ページも利用可能ページだった場合、バディーシステムへの登録の際、6のオーダで登録するかどうかの判断のようです。(たぶん)
で、while (start < end)で、全ページフレームをチェックします。idx = start - bdata->node_min_pfnでチェックするページフレームの0オリジンとするインデックスで、vec = ~map[idx / BITS_PER_LONG]で対応する32ページフレームのビットとチェックすることで、そのインデックスに対する32個のページフレームが利用可能かどうかのチェックとなるわけです。(この辺りのビット処理での実装は凡人には思いつかないな。と感心させられます。)
そうなら、一挙にorder = ilog2(BITS_PER_LONG)のオーダで(たぶんBITS_PER_LONGが32として6?)、__free_pages_bootmem()でバディーシステムに登録です。
そでないなら、BITS_PER_LONG回、vec >>= 1でビットマップをチェックすることで、1ページづつ__free_pages_bootmem()でバディーシステムに登録しているようです。
whileループを抜けた後の処理は、bdata->node_bootmem_mapで使っていたページフレームは用済みとなったので、本ページを利用可能ページとしてバディーシステムに登録しています(実にきっちりしています。)page = virt_to_page(bdata->node_bootmem_map)はbdata->node_bootmem_mapが使用している先頭ページです。pages = bdata->node_low_pfn - bdata->node_min_pfnはチェックするページ数、すなわちbdata->node_bootmem_mapのサイズが決定し、従ってbdata->node_bootmem_mapで使用されていたページ数が算出しています。そしてそのページをすべてバディーシステムに登録しています。
バディーシステムに登録し全ページはcountに保持されています。すなわちこれがここでの全メモリ(ページ数)というわけです。
static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)
{
int aligned;
struct page *page;
unsigned long start, end, pages, count = 0;
if (!bdata->node_bootmem_map)
return 0;
start = bdata->node_min_pfn;
end = bdata->node_low_pfn;
aligned = !(start & (BITS_PER_LONG - 1));
bdebug("nid=%td start=%lx end=%lx aligned=%d\n",
bdata - bootmem_node_data, start, end, aligned);
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);
bdebug("nid=%td released=%lx\n", bdata - bootmem_node_data, count);
return count;
}
なお、set_highmem_pages_init()関数では、ハイメモリ空間のページ数の処理をして、そのページ数もバディーシステムに登録しtotalram_pagesに足しこんでいます。そしてrest_init()関数からkernel_initスレッドが起動され其処から呼ばれるfree_initmem()で、カーネル初期化等で再使用されないメモリー空間も同様な処理を行っています。
static void noinline __init_refok rest_init(void)
__releases(kernel_lock)
{
:
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
:
}
static int __init kernel_init(void * unused)
{
:
init_post();
return 0;
}
static int noinline init_post(void)
{
free_initmem();
:
}
void free_initmem(void)
{
free_init_pages("unused kernel memory",
(unsigned long)(&__init_begin),
(unsigned long)(&__init_end));
}
全メモリーは実際の物理メモリ量というのでなく、バディーシステムに登録されるページ数とも言えそうです。




