Advent Calendar 2018: advos
Day 11: メモリ管理(バディシステムの初期化)
アーキテクチャ依存コードの整理
カーネルはアーキテクチャ依存のコードがたくさん出てきます。アーキテクチャ依存コードはsrc/kernel/arch/x86_64
に置くようにします。src/kernel/kernel.c
のアーキテクチャ依存コードも徐々に移していこうと思います。まず,手始めに,src/kernel/kasm.S
をsrc/kernel/arch/x86_64/asm.S
に移動します。
バディシステムの実装
今日は昨日の残課題になっていたバディシステムの実装をしていきます。
昨日説明した以下の3つのゾーンのうち,ZONE_DMAとZONE_KERNELをコアゾーンと呼ぶことにします。
- ZONE_DMA: 0–16 MiB
- ZONE_KERNEL: 16–64 MiB
- ZONE_NUMA_AWARE: 64– MiB
コアゾーンは簡単のため,メモリ管理システム用のメモリ領域(コアゾーンのバディシステムのポインタ情報などを保存する領域)として,0x00068000-0x00068fff
を割り当てます。
Start | End | Description |
---|---|---|
00000500 | 00007bff | ブートローダのスタック領域 |
00007c00 | 00007dff | MBR |
00007e00 | 00007fff | 未使用 |
00008000 | 00008fff | ブート情報(ドライブ番号など) |
00009000 | 0000cfff | ブートモニタ (16 KiB) |
0000d000 | 0000ffff | 未使用 (ブートモニタ拡張用に予約) |
00010000 | 0002ffff | カーネル (最大128 KiB) |
00030000 | 00067fff | 予約 |
00068000 | 00068fff | コアゾーン管理用領域 |
00069000 | 0006ffff | カーネルのベースページテーブル |
00070000 | 00073fff | 予約:trampoline (16 KiB) |
00074000 | 00075fff | 予約:GDT (8 KiB) |
00076000 | 00077fff | 予約:IDT (8 KiB) |
00078000 | 00078fff | 予約 |
00079000 | 0007ffff | ロングモード用ページテーブル (24 KiB = 6 * 4 KiB以上必要) |
バディシステムの初期化
最もストレートフォワードなバディシステムの初期化方法は,INT 15h, AX=E820hのメモリマップでusableな領域を1ページずつ追加する方法です。しかしこの方法は,大容量のメモリの初期化に非常に時間がかかります。コアゾーンだけなら問題無いですが,NUMA-awareなゾーンにもコードを再利用できるようにするため,初期化は工夫します。
あるメモリ領域base
からnext-1
までをバディーシステムに登録する関数phys_mem_buddy_add_regions()
を以下の用に実装します。以下のコードは全て仮想メモリのアドレスとして扱います。昨日説明した通り,リニアマッピングを採用しているため,物理メモリアドレスは仮想メモリへのリニアマッピングのオフセットから計算できます。
void
phys_mem_buddy_add_region(phys_memory_buddy_page_t **buddy, uintptr_t base,
uintptr_t next)
{
_add_region_order(buddy, MEMORY_PHYS_BUDDY_ORDER, base, next);
}
第一引数のbuddy
はバディシステムの最大オーダー+1個のポインタの配列です。コアゾーンについては,src/kernel/memory.h
に以下のように構造体のメンバとして定義しています。
#define MEMORY_ZONE_CORE_NUM 2
phys_memory_buddy_page_t *heads[MEMORY_PHYS_BUDDY_ORDER + 1];
関数phys_mem_buddy_add_regions()
から今回のバディシステムでサポートする最大のブロック長1 GiB(オーダー=18)に,追加するメモリ領域が当てはまるかどうかを確認し,当てはまらなければ一段下のオーダーを再帰的に確認するコードが以下の部分です。
static void
_add_region_order(phys_memory_buddy_page_t **buddy, int order,
uintptr_t base, uintptr_t next)
{
uint64_t blocksize;
uintptr_t base_aligned;
uintptr_t next_aligned;
uintptr_t ptr;
int i;
int nr;
/* May happen if base or next is not 4 KiB-alinged */
if ( order < 0 ) {
return;
}
/* The size of the block for this order */
blocksize = (1ULL << order) * MEMORY_PAGESIZE;
/* Align the base and next addresses */
base_aligned = (base + (blocksize - 1)) & ~(blocksize - 1);
next_aligned = next & ~(blocksize - 1);
/* Add smaller chunk to the lower order of the buddy system */
if ( base != base_aligned ) {
ptr = base_aligned < next ? base_aligned : next;
_add_region_order(buddy, order - 1, base, ptr);
}
if ( next != next_aligned && next_aligned >= base ) {
ptr = next_aligned > base ? next_aligned : base;
_add_region_order(buddy, order - 1, ptr, next);
}
/* Add pages to this zone */
nr = ((next_aligned - base_aligned) >> order) / MEMORY_PAGESIZE;
for ( i = 0; i < nr; i++ ) {
_add_block(buddy, order, base_aligned + i * blocksize);
}
}
ブロック長でアライメントを取り,そのアライメントにはまらなかった領域を一段下のオーダーで再確認します。このオーダーでアライメントの取れる領域は,ブロックとして追加します。以下のコードは,オーダーorder
の連結リストをアドレス順でのソートを保ったまま,addr
のブロックを連結リストの適切な場所に挿入するロジックです。
static void
_add_block(phys_memory_buddy_page_t **buddy, int order, uintptr_t addr)
{
phys_memory_buddy_page_t *page;
phys_memory_buddy_page_t **cur;
page = (phys_memory_buddy_page_t *)addr;
page->next = NULL;
/* Keep the linked list sorted */
cur = &buddy[order];
while ( *cur ) {
if ( addr < (uint64_t)(*cur) ) {
/* Insert here */
page->next = *cur;
*cur = page;
return;
}
cur = &(*cur)->next;
}
/* If reaches at the end of the list */
*cur = page;
}
まとめと明日の予定
今日はバディーシステムの初期化を実装しました。明日はバディシステムから物理メモリの割り当てと解放を実装しようと思います。