トータルメモリ


システムの全メモリーはproc/meminfoのMemTotal:項目で表示されまが、一体この値はどのように取得されているのでしょうか?
[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));
}
全メモリーは実際の物理メモリ量というのでなく、バディーシステムに登録されるページ数とも言えそうです。

最終更新 2011/07/13 13:01:59 - north
(2011/07/13 05:11:51 作成)


検索

アクセス数
3697969
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。