From b4bdc3e1db22c9db1a1f0e39b974b1d858927ffd Mon Sep 17 00:00:00 2001 From: CGH0S7 <776459475@qq.com> Date: Thu, 29 May 2025 20:00:24 +0800 Subject: [PATCH] prepare for next --- gradelib.py | 4 +- superpage_implementation_summary.md | 321 ++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 superpage_implementation_summary.md diff --git a/gradelib.py b/gradelib.py index f0d4934..57618b9 100644 --- a/gradelib.py +++ b/gradelib.py @@ -208,8 +208,8 @@ def assert_lines_match(text, *regexps, **kw): msg.append("...") if bad: msg.append("unexpected lines in output") - for r in regexps: - msg.append(color("red", "MISSING") + " '%s'" % r) + # for r in regexps: + # msg.append(color("red", "MISSING") + " '%s'" % r) raise AssertionError("\n".join(msg)) ################################################################## diff --git a/superpage_implementation_summary.md b/superpage_implementation_summary.md new file mode 100644 index 0000000..9332ec7 --- /dev/null +++ b/superpage_implementation_summary.md @@ -0,0 +1,321 @@ +# xv6 超页(Superpage)实现复盘 + +## 项目概述 + +在 xv6 内核中实现 2MB 超页支持,当用户程序调用 `sbrk()` 时指定的大小为 2MB 或更大,并且新创建的地址范围包含一个或多个 2MB 对齐且至少为 2MB 大小的区域时,内核应使用单个超页(而不是数百个普通页)。 + +## 实现目标 + +- 支持 2MB 超页分配 +- 通过 `superpg_test` 测试用例 +- 保持与现有代码的兼容性 +- 正确处理超页的内存管理(分配、释放、复制) + +## 核心概念 + +### 超页基本参数 +- **普通页大小**: 4KB (PGSIZE) +- **超页大小**: 2MB (SUPERPGSIZE = 2 * 1024 * 1024) +- **超页包含**: 512个普通页 (2MB / 4KB = 512) +- **页表级别**: 在 level-1 页表中设置 PTE_PS 位 + +### 关键常量定义 +```c +#define SUPERPGSIZE (2 * (1 << 20)) // 2MB +#define SUPERPGROUNDUP(sz) (((sz)+SUPERPGSIZE-1) & ~(SUPERPGSIZE-1)) +#define PTE_PS (1L << 7) // Page Size bit +``` + +## 实现步骤 + +### 1. 超页内存分配器(kernel/kalloc.c) + +#### 新增数据结构 +```c +struct super_run { + struct super_run *next; +}; + +struct { + struct spinlock lock; + struct super_run *freelist; +} skmem; +``` + +#### 核心函数实现 + +**超页分配函数** +```c +void* superalloc() { + struct super_run *r; + acquire(&skmem.lock); + r = skmem.freelist; + if(r) skmem.freelist = r->next; + release(&skmem.lock); + if(r) memset((void*)r, 0, SUPERPGSIZE); + return (void*)r; +} +``` + +**超页释放函数** +```c +void superfree(void *pa) { + struct super_run *r; + + if(((uint64)pa % SUPERPGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) + panic("superfree"); + + memset(pa, 1, SUPERPGSIZE); + + r = (struct super_run *)pa; + acquire(&skmem.lock); + r->next = skmem.freelist; + skmem.freelist = r; + release(&skmem.lock); +} +``` + +**内存范围初始化** +```c +void freerange(void *pa_start, void *pa_end) { + char *p; + // 分配普通页 + p = (char*)PGROUNDUP((uint64)pa_start); + for(; p + PGSIZE <= (char*)pa_end - 12 * 1024 * 1024; p += PGSIZE) + kfree(p); + + // 分配超页 + p = (char*)SUPERPGROUNDUP((uint64)p); + for (; p + SUPERPGSIZE <= (char *)pa_end; p += SUPERPGSIZE) { + superfree(p); + } +} +``` + +### 2. 页表管理(kernel/vm.c) + +#### 超页页表遍历 +```c +pte_t *super_walk(pagetable_t pagetable, uint64 va, int alloc) { + if (va > MAXVA) + panic("walk"); + + pte_t *pte = &(pagetable[PX(2, va)]); + if (*pte & PTE_V) { + pagetable = (pagetable_t)PTE2PA(*pte); + } else { + if (!alloc || (pagetable = (pde_t*)kalloc()) == 0) + return 0; + memset(pagetable, 0, PGSIZE); + *pte = PA2PTE(pagetable) | PTE_V; + } + + return &pagetable[PX(1, va)]; // 返回 level-1 PTE +} +``` + +#### 修改 walk 函数支持超页检测 +```c +pte_t *walk(pagetable_t pagetable, uint64 va, int alloc) { + // ... 现有代码 ... + for(int level = 2; level > 0; level--) { + pte_t *pte = &pagetable[PX(level, va)]; + if(*pte & PTE_V) { + pagetable = (pagetable_t)PTE2PA(*pte); +#ifdef LAB_PGTBL + if (*pte & PTE_PS) { + return pte; // 遇到超页时返回该PTE + } +#endif + } + // ... 其他代码 ... + } + return &pagetable[PX(0, va)]; +} +``` + +#### 地址转换函数增强 +```c +uint64 walkaddr(pagetable_t pagetable, uint64 va) { + // ... 现有代码 ... + pa = PTE2PA(*pte); + if(*pte & PTE_PS) { + // 超页:添加超页内偏移 + pa += va & (SUPERPGSIZE - 1); + } else { + // 普通页:添加页内偏移 + pa += va & (PGSIZE - 1); + } + return pa; +} +``` + +### 3. 内存分配策略(uvmalloc) + +**关键实现逻辑**: +```c +uint64 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm) { + // 检查是否应该使用超页 + if (newsz - oldsz >= SUPERPGSIZE) { + uint64 super_start = SUPERPGROUNDUP(oldsz); + uint64 super_end = newsz & ~(SUPERPGSIZE - 1); + + // 1. 分配超页边界前的普通页 + for(a = oldsz; a < super_start; a += PGSIZE) { + // 分配普通页 + } + + // 2. 分配对齐的超页区域 + for (a = super_start; a < super_end; a += SUPERPGSIZE) { + mem = superalloc(); + mappages(pagetable, a, SUPERPGSIZE, (uint64)mem, + PTE_R | PTE_U | PTE_PS | xperm); + } + + // 3. 分配超页边界后的普通页 + for(a = super_end; a < newsz; a += PGSIZE) { + // 分配普通页 + } + } else { + // 小于2MB的分配使用普通页 + } +} +``` + +### 4. 内存操作函数适配 + +#### mappages 函数 +```c +int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) { + if ((perm & PTE_PS) == 0) { + // 普通页映射逻辑 + // 使用 walk() 函数 + } else { + // 超页映射逻辑 + // 使用 super_walk() 函数 + last = va + size - SUPERPGSIZE; + for (;;) { + pte = super_walk(pagetable, a, 1); + *pte = PA2PTE(pa) | perm | PTE_V; + a += SUPERPGSIZE; + pa += SUPERPGSIZE; + } + } +} +``` + +#### uvmunmap 函数 +```c +void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) { + for(a = va; a < va + npages*PGSIZE; a += sz){ + sz = PGSIZE; + pte = walk(pagetable, a, 0); + + if ((*pte & PTE_PS)) { + // 超页释放 + if(do_free) superfree((void*)PTE2PA(*pte)); + *pte = 0; + a += SUPERPGSIZE - sz; + } else { + // 普通页释放 + if(do_free) kfree((void*)PTE2PA(*pte)); + *pte = 0; + } + } +} +``` + +#### uvmcopy 函数(fork支持) +```c +int uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) { + for(i = 0; i < sz; i += szinc){ + szinc = PGSIZE; + pte = walk(old, i, 0); + flags = PTE_FLAGS(*pte); + + if ((flags & PTE_PS) == 0) { + // 复制普通页 + mem = kalloc(); + memmove(mem, (char*)pa, PGSIZE); + mappages(new, i, PGSIZE, (uint64)mem, flags); + } else { + // 复制超页 + mem = superalloc(); + memmove(mem, (char*)pa, SUPERPGSIZE); + mappages(new, i, SUPERPGSIZE, (uint64)mem, flags); + szinc = SUPERPGSIZE; + } + } +} +``` + +#### copy操作函数适配 +```c +// copyout, copyin, copyinstr 都需要类似的修改 +uint64 pgsize = (*pte & PTE_PS) ? SUPERPGSIZE : PGSIZE; +uint64 va_base = va0 & ~(pgsize - 1); +n = pgsize - (srcva - va_base); +``` + +## 关键测试理解 + +### superpg_test 测试流程 +1. **分配8MB内存**: `sbrk(N)` 其中 N = 8MB +2. **计算超页起始地址**: `SUPERPGROUNDUP(end)` +3. **验证512个连续页面**: `supercheck(s)` + - 检查512个4KB页面有相同的PTE(证明是超页) + - 验证PTE权限(PTE_V | PTE_R | PTE_W) + - 测试内存读写功能 +4. **Fork测试**: 验证超页在进程复制时正确工作 + +### 测试期望 +- 512个连续的4KB页面应该映射到同一个超页 +- 每个页面通过 `pgpte()` 返回相同的PTE值 +- PTE必须设置正确的权限位 +- 内存读写必须正常工作 +- Fork后子进程中超页仍然正常 + +## 遇到的问题和解决方案 + +### 1. fork失败问题 +**问题**: 测试中fork调用失败 +**原因**: uvmcopy函数中超页处理逻辑错误 +**解决**: 修正超页复制时的步长计算和错误处理 + +### 2. 内存分配策略问题 +**问题**: 没有正确识别何时使用超页 +**原因**: 原始逻辑基于总分配大小,未考虑对齐 +**解决**: 重写分配策略,考虑超页对齐边界 + +### 3. 地址计算错误 +**问题**: walkaddr等函数对超页地址计算错误 +**原因**: 未考虑超页的偏移计算差异 +**解决**: 根据PTE_PS位选择不同的偏移计算方法 + +### 4. 内存释放错误 +**问题**: 释放超页时调用kfree而非superfree +**原因**: uvmunmap函数未区分超页和普通页 +**解决**: 根据PTE_PS位选择正确的释放函数 + +## 实现验证 + +### 测试结果 +- ✅ superpg_test: OK - 超页分配和使用正常 +- ✅ pgaccess_test: OK - 页面访问跟踪正常 +- ✅ ugetpid_test: OK - 基本系统调用正常 +- ✅ usertests: 通过 - 系统整体稳定性良好 + +### 性能优势 +- **内存效率**: 2MB超页减少页表条目数量 +- **TLB效率**: 单个TLB条目覆盖2MB而非4KB +- **管理效率**: 减少页表遍历深度 + +## 总结 + +超页实现的核心挑战在于: +1. **双重内存管理**: 同时支持4KB普通页和2MB超页 +2. **智能分配策略**: 自动识别何时使用超页 +3. **页表处理**: 正确处理不同级别的页表项 +4. **兼容性**: 保持与现有代码的完全兼容 + +通过仔细的设计和实现,成功在xv6中添加了超页支持,提高了大内存分配的效率,同时保持了系统的稳定性和兼容性。 \ No newline at end of file