diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-10-16 02:39:56 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-10-16 02:39:56 +0800 |
| commit | 8cace3f05003c6cb4d38c36890055f03151c2ad4 (patch) | |
| tree | aa739deaac0266f17c4e91b333957501cb1ea2e9 /docs/specs/pmm.md | |
| parent | 3ad39e9878310e5317ee73c9b353c1e2c1c5c4ee (diff) | |
| download | lume-8cace3f05003c6cb4d38c36890055f03151c2ad4.tar.gz lume-8cace3f05003c6cb4d38c36890055f03151c2ad4.tar.xz | |
Remove old code
Signed-off-by: Richard Qin <richard.qin.x@gmail.com>
Diffstat (limited to 'docs/specs/pmm.md')
| -rw-r--r-- | docs/specs/pmm.md | 209 |
1 files changed, 0 insertions, 209 deletions
diff --git a/docs/specs/pmm.md b/docs/specs/pmm.md deleted file mode 100644 index 5703e7a..0000000 --- a/docs/specs/pmm.md +++ /dev/null @@ -1,209 +0,0 @@ -# 物理内存管理器 (Physical Memory Manager) - -## 1. 目标 (Objective) - -为 LumeOS 提供对物理内存页帧(Frame)的分配与回收服务。本模块基于伙伴系统(Buddy System)算法管理所有可用物理内存,并引入 Per-CPU 缓存(PCP)机制以降低多核场景下的锁竞争。PMM 是 Slab 分配器和 VMM 的底层基石。 - -## 2. 前置依赖 (Dependencies) - -- **全局初始化契约 (`global_init.md`):** `pmm_init()` 在 `fdt_init()` 之后、`slab_init()` 之前被 BSP 调用。 -- **设备树契约 (`fdt.md`):** 依赖 `fdt_early_get_mem_info()` 获取物理内存的基址和大小。 -- **并发同步契约 (`spinlock.md`):** 依赖 `Spinlock` 实现对全局伙伴系统的互斥访问。 -- **架构抽象契约 (`arch_abstraction.md`):** 依赖 `arch::cpu::id()` 获取当前 CPU 编号以访问 Per-CPU 缓存;依赖 `arch::cpu::intr_off()/intr_on()` 保护 PCP 操作免受中断重入。 -- **项目约定 (`00_conventions.md`):** 遵循所有命名、错误处理和 API 设计规范。 - -## 3. 核心数据结构 (Data Structures) - -### 3.1 物理页帧描述符 (`struct Frame`) - -为了极致地压缩元数据内存占用,`struct Frame` 采用 `union` 设计,并严格保证 `refcount` 独立于 `union` 之外,以支持 COW。 - -```cpp -// 定义于 include/lume/frame.h -#include <lume/types.h> -#include <lume/list.h> - -class KmemCache; // 前向声明 - -enum class FrameState : uint8 { - Free, // 空闲,位于 Buddy 或 PCP 链表中 - Allocated, // 已分配给上层(Slab, VMM) - Slab, // 作为 Slab 页使用 -}; - -struct Frame { - uint32 refcount; // 原子引用计数(独立于 union) - FrameState state; // 页帧当前状态 - uint8 order; // 伙伴系统中的阶数 (0-10 for 4KB-4MB) - - union { - // 当 state == FrameState::Free 时 - struct { - list_node free_link; // 用于挂载到 Buddy 或 PCP 的空闲链表 - } free; - - // 当 state == FrameState::Slab 时 - struct { - KmemCache* cache; // 指向所属的 Slab 缓存 - uint32 obj_count; // 已分配对象数量 - } slab; - }; -}; -``` - -### 3.2 Per-CPU 单页缓存 (`struct PerCpuCache`) - -每个 CPU 私有的单页帧空闲链表。**访问 PCP 时必须关闭中断**,防止定时器中断处理函数在同一 CPU 上重入 PCP 操作导致链表损坏。 - -```cpp -// 定义于 mm/pmm.cc (模块私有) -inline constexpr int kPcpHighWatermark = 64; // PCP 缓存上限 -inline constexpr int kPcpBatchSize = 32; // 每次 refill/drain 的批量大小 - -struct PerCpuCache { - list_node free_list; // 单页帧空闲链表头 - uint32 count; // 当前缓存的页帧数量 -}; -``` - -### 3.3 伙伴系统常量 - -```cpp -// 定义于 include/lume/pmm.h -inline constexpr int kMaxOrder = 11; // 阶数 0..10 → 4KB..4MB -``` - -## 4. 公共 API (Public APIs) - -所有分配函数在失败时返回 `nullptr`。 - -```cpp -// === 初始化 === -void pmm_init(); // BSP 调用,从 FDT 发现内存并初始化伙伴系统 - -// === 单页帧操作 (Fast Path via PCP) === -Frame* pmm_alloc_frame(); // 分配一个 4KB 页帧 -void pmm_free_frame(Frame* f); // 释放一个 4KB 页帧 - -// === 多页帧操作 (Slow Path, 直接伙伴系统) === -Frame* pmm_alloc_frames(uint8 order); // 分配 2^order 个连续页帧 -void pmm_free_frames(Frame* f, uint8 order); // 释放 2^order 个连续页帧 - -// === 引用计数操作 (原子, 无锁) === -void frame_incref(Frame* f); // 原子递增引用计数 -void frame_decref(Frame* f); // 原子递减,降至 0 时自动释放 - -// === 地址转换 === -uint64 frame_to_pa(const Frame* f); // Frame* → 物理地址 -Frame* pa_to_frame(uint64 pa); // 物理地址 → Frame* -uint64 frame_to_va(const Frame* f); // Frame* → 内核虚拟地址 -``` - -## 5. 并发与锁契约 (Concurrency & Locks) - -- **持有锁:** 模块内部持有一把 `Spinlock` (`g_pmm_lock`)。 - -- **锁的作用域:** `g_pmm_lock` **仅保护**全局伙伴系统 (`g_free_areas[]`)。 - -- **PCP 保护机制:** PCP 操作**必须在中断关闭状态下**执行。这不是"无锁"——而是通过关中断防止同一 CPU 上的重入(定时器中断 → IRQ handler → pmm_alloc_frame → 损坏 PCP 链表)。关中断由 `pmm_alloc_frame()`/`pmm_free_frame()` 内部自动管理。 - -- **加锁慢速路径:** - - 当 PCP 为空时,`refill_pcp()` 获取 `g_pmm_lock` 从伙伴系统批量取页。 - - 当 PCP 达到 `kPcpHighWatermark` 时,`drain_pcp()` 获取 `g_pmm_lock` 归还半数页帧。 - - `pmm_alloc_frames()` 和 `pmm_free_frames()` 因直接操作伙伴系统,**始终需要**获取 `g_pmm_lock`。 - -- **原子操作:** `frame_incref()` / `frame_decref()` 使用 `__atomic_fetch_add/sub` + `__ATOMIC_ACQ_REL`。 - -## 6. 错误路径 (Error Paths) - -- **初始化失败:** `pmm_init()` 若无法从 FDT 找到可用内存,或无法为 `frame_map` 分配空间,则 `kernel_panic()`。 - -- **分配失败:** `pmm_alloc_*()` 在物理内存耗尽时返回 `nullptr`。上层负责处理。 - -- **释放错误:** `pmm_free_*()` 若接收到 `nullptr`,立即 `kernel_panic()`。 - -## 7. 状态机 / 时序图 (State Machine / Sequence) - -### 7.1 单页分配时序 (Fast & Slow Path) - -``` -[pmm_alloc_frame() on CPU X] - │ - ├─> 1. 关闭中断 (防止同 CPU 重入) - │ - ├─> 2. 获取 pcp = &g_pcp[cpu_id] - │ - ├─> 3. if (pcp->count > 0) ? - │ ├── [是 (Fast Path)] - │ │ ├─ 3.1 从 pcp->free_list 弹出一个 Frame - │ │ ├─ 3.2 设置 state=Allocated, refcount=1 - │ │ └─ 3.3 恢复中断, return Frame - │ │ - │ └── [否 (Slow Path)] - │ ├─ 4.1 LockGuard guard(g_pmm_lock); - │ ├─ 4.2 refill_pcp(pcp); // 批量从 Buddy 取 kPcpBatchSize 页 - │ ├─ 4.3 释放锁 - │ └─ 4.4 回到步骤 3 重试 - │ - ▼ -[恢复中断, 返回 Frame* 或 nullptr] -``` - -### 7.2 初始化时序 - -``` -[pmm_init() — BSP 单核期] - │ - ├─> 1. fdt_early_get_mem_info(&base, &size) - │ └─ 获取物理内存基址和大小 - │ - ├─> 2. 计算 frame_map 位置 (紧跟 _kernel_end 之后, 页对齐) - │ └─ frame_map 大小 = num_frames * sizeof(Frame) - │ - ├─> 3. 清零 frame_map - │ - ├─> 4. 初始化 Spinlock 和 Buddy 空闲链表头 - │ - ├─> 5. 初始化所有 PCP 链表头 - │ - └─> 6. 将所有空闲页帧 (frame_map 之后) 按最大对齐阶数插入 Buddy -``` - -### 7.3 写时复制 (COW) 引用计数流程 - -``` -[父进程 fork() 创建子进程] - │ - ├─> 1. 遍历父进程所有可写 VMA - │ - ├─> 2. for each VMA: - │ ├─ 2.1 复制父进程的 PTE 到子进程页表 - │ ├─ 2.2 清除父、子 PTE 的 'Write' 权限位 - │ ├─ 2.3 设置父、子 PTE 的 'COW' 软件标志位 - │ └─ 2.4 frame_incref(pa_to_frame(PTE->paddr)); - │ - ▼ -[子进程尝试写入共享页面] - │ - ├─> 1. 触发 Store Page Fault - │ - ├─> 2. VMM 陷阱处理: - │ ├─ 2.1 检查 PTE,确认是合法的 COW Fault - │ ├─ 2.2 old_frame = pa_to_frame(PTE->paddr) - │ ├─ 2.3 if (old_frame->refcount == 1) ? - │ │ ├── [是] 直接恢复 Write 权限,无需拷贝 - │ │ └── [否] 分配新页, 拷贝, 修改 PTE, frame_decref(old) - │ - ▼ -[返回用户态,写操作成功] -``` - -## 8. 已知缺陷与未来挑战 (Known Weaknesses & Future Challenges) - -### 8.1 内存回收 - -当前 API 在物理内存耗尽时仅返回 `nullptr`,缺乏主动回收机制。未来需引入内存回收器。 - -### 8.2 frame_map 存储 - -当前设计假设单一连续 `frame_map` 数组,适用于 QEMU 线性内存。对于多内存区域的真实硬件,需演进为稀疏内存模型。 |
