虛擬記憶體是現代作業系統核心子系統,透過「邏輯位址空間與物理記憶體解耦」,實現進程隔離、記憶體共享、按需分頁等關鍵能力。本文從基本概念、頁式管理、TLB 加速、多級頁表到 Harvard OS161 真實內核代碼,層層深入剖析虛擬記憶體實現細節。
進程建立時,OS 為所有頁分配頁框;運行中動態申請空間時再分配。頁表項(PTE)記錄「虛擬頁號 → 物理頁框號」映射及權限位。但頁表駐留記憶體,每次記憶體訪問需兩次查表(查頁表 + 訪問資料),效率大幅下降。
TLB 是 MMU 內部的高速快取,儲存熱門頁表項。基於局部性原理,程式訪問臨近程式碼機率高,TLB 命中率可達 99%+,極大緩解「兩次訪問」問題。TLB 容量有限(典型 64-512 條),採用全相聯或組相聯映射。
32 位系統單級頁表需 4MB(1M 頁 × 4B),過大。二級頁表將頁號拆為「目錄索引 + 頁表索引」,一級頁表僅存儲二級頁表指標,未分配區域不分配二級頁表,大幅節省空間,代價是增加一次查表。
OS161 基於 MIPS-I,採用二級頁表,代碼倉庫:
https://github.com/tian-jiang/OS161-VirtualMemory
/* arch/mips/include/vm.h */ #define MIPS_KUSEG 0x00000000 // 用戶空間,TLB 映射 #define MIPS_KSEG0 0x80000000 // 核心非快取映射 #define MIPS_KSEG1 0xa0000000 // 核心非快取非映射 #define MIPS_KSEG2 0xc0000000 // 核心 TLB 映射
void ram_bootstrap(void) {
size_t ramsize = mainbus_ramsize();
if (ramsize > 508*1024*1024) ramsize = 508*1024*1024; // MIPS 限制
lastpaddr = ramsize;
firstpaddr = firstfree - MIPS_KSEG0; // firstfree 來自 start.S
kprintf("%uk physical memory available\n", (lastpaddr-firstpaddr)/1024);
}
功能:探測物理記憶體大小,計算首個可用物理頁框 firstpaddr 與末尾 lastpaddr。
void frametable_bootstrap(void) {
paddr_t firsta, lasta;
ram_getsize(&firsta, &lasta); // 獲取可用物理範圍
size_t framenum = (lasta - firsta) / PAGE_SIZE; // 總頁框數
size_t frame_table_size = ROUNDUP(framenum * sizeof(struct frame_table_entry), PAGE_SIZE);
size_t entry_num = frame_table_size / PAGE_SIZE; // 幀表自身佔用的頁數
frametop = firsta;
freeframe = firsta + frame_table_size; // 空閒頁框從幀表後開始
frame_table = (struct frame_table_entry *)PADDR_TO_KVADDR(firsta); // 幀表置於物理記憶體起始處
// 初始化空閒鏈表:前 entry_num 個項對應幀表自身,標記為已用
for (i = 0; i < framenum-1; i++) {
if (i < entry_num) { p->next_freeframe = 0; }
else { p->next_freeframe = frametop + (i+1)*PAGE_SIZE; }
p++;
}
}
關鍵設計:幀表本身佔用物理記憶體起始區域,每項對應一個頁框,next_freeframe=0 表示已分配,非零指向下一個空閒頁框,形成單向鏈表。
struct frame_table_entry {
size_t next_freeframe; // 下一個空閒頁框物理位址,0 表已分配
};
虛擬記憶體透過「分頁 + 頁表 + TLB + 多級頁表 + 頁面置換」協同工作,在有限物理記憶體上構建無限大的邏輯空間假象。OS161 代碼展示了教學級內核如何以清晰模組化方式實現這一複雜子系統,是理解作業系統記憶體管理的最佳實踐範本。