内存管理测试
内存测试覆盖区间事实、资源所有权、失败回滚、跨 CPU 可见性和热路径延迟。Host 测试使用可控分配器、页表、后端和记录型 DMA 适配器;板级测试覆盖固件内存图、缓存、Translation Lookaside Buffer(地址转换后备缓冲区,TLB)与设备行为。
1. 启动与 allocator 测试
启动内存和 allocator 的错误会污染所有上层测试,因此需要先验证物理总量和不重叠,再测试分配行为。
1.1 启动内存图
确定性输入应包含至少两段 RAM、一个跨 Free 中部的 reservation、KImage、early bump used prefix 和 MMIO hole。输出逐段比较,而不是只比较总大小。
| 用例 | 断言 |
|---|---|
| Free 中插入 Reserved | Free 被 split,Reserved 精确保留 |
| 相邻同类型 reservation | 合并为单一描述符 |
| 不同 non-Free overlap | 返回 RangeError::Conflict,新描述符未加入 |
| fixed capacity exhausted | 返回 RangeError::Capacity;注意 merge_add 原地修改,此前拆分不回滚,启动路径应立即 panic |
| range/alignment overflow | 返回 typed error,无 wrapping range |
| 多 memory nodes/regions | 每个合法 bank 都进入最终 Free 列表 |
| early bump 交接 | memory_map_setup() 把 used prefix 变 Reserved;memories() 超过 128 ranges 时 .ok() 静默截断应被测试暴露 |
| 多段 RAM 与首个大段 | early arena 是排序后第一个大于 8 MiB 的 Free 段(与架构、地址高低无关);无候选时启动失败 |
| 12 GiB 固件保留区 | 启动内存图只增加一个区间描述符,不按 4 KiB 展开 |
| RAM、MMIO 与地址空洞分类 | 只有 Free RAM 进入 Buddy;设备区使用设备属性;不可访问区不误作普通 RAM |
板级启动日志应保存固件输入、someboot memory map、ax-hal normalized regions和 Buddy managed sections,逐层核对丢失的 bytes 属于哪类 metadata或对齐。
1.2 页与小对象分配
host test 使用可控内存 slice 建立多个 section,覆盖 page、lowmem、small object、large object 和 remote-free。
| 用例 | 断言 |
|---|---|
| add disjoint regions | section count 和 managed bytes正确 |
| add overlap | 返回 MemoryOverlap |
| too-small added region | 明确 skip,不产生无效 section |
| Normal page | size/alignment 满足,free 后可合并 |
| Dma32 page | allocation 最后一个 byte 小于 4 GiB |
| large contiguous | 不跨 section;无单 section 可满足时 内存不足 |
| all size classes | round-up、bitmap 和 empty-slab return正确 |
| cross-CPU free | remote stack只消费一次,owner drain 后可复用 |
字节分配失败不触发 callback、虚拟文件系统、阻塞或隐式 retry。显式页分配(alloc_pages() / alloc_dma32_pages())失败则会在释放 allocator 锁后调用已注册的 page reclaim callback 并最多重试 4 轮;使用 fake reclaim counter 可以验证“锁外执行、重试有界、回收 0 页即返回 NoMemory”。
2. 页表与地址空间测试
页表测试验证递归映射、页表帧和失效机制,地址空间测试验证区域元数据与具体 backend 的组合语义。不同 backend 的回滚边界并不相同,因此测试必须分别固定 ArceOS、StarryOS 和 Axvisor 的实际资源结果,不能用一个笼统的 all-or-rollback 预期替代。
2.1 页表能力
每个架构 entry 应做 flags/页表项 round-trip,每个 engine 应做 map/query/protect/unmap 和 ownership teardown。
| 用例 | 断言 |
|---|---|
| entry round-trip | 物理地址、permission、device/uncached 与 huge bit 不丢失 |
| AArch64 MAIR layout | 运行时 3 slot(0x44ff04)与 boot 4 slot 布局中 index 0/1/2 语义一致 |
| frame allocation failure | 返回 PagingError::NoMemory,不留下 half-linked table |
| map conflict | 旧页表项保持不变 |
| huge mapping | alignment、level 和 translate offset正确 |
| Guest huge Linear clear | 按实际 block 数保存恢复项;clear 成功且不退化为逐 4 KiB 快照 |
| 混合页尺寸 range | 未对齐首尾使用 4 KiB,中间在允许时升级为 2 MiB/1 GiB |
| 属性边界 | 大页不跨越权限、缓存属性或所有权发生变化的边界 |
| map_region 批量失效 | ≤ TARGETED_FLUSH_LIMIT(32)个新映射逐地址 flush,超过阈值一次 full flush;protect/remap 立即逐地址 flush |
| 多核 shootdown | 解除共享内核映射走 flush_tlb_range_all_cpus(),未 ready/offline CPU 被跳过(CpuOffline) |
| owned table Drop | 每个 owned child frame只释放一次,Drop 不隐式 flush |
主机第一阶段、客户机第二阶段和 boot 页表需要分别构建,证明所属 crate 之间没有错误耦合。boot provider 的 no-free 语义应单独测试,不能用 runtime provider 的 Drop 预期套用。
2.2 虚拟区域与 backend 操作
ax-memory-set 测试覆盖重叠检测、split/shrink、直接 map/unmap/protect 和 metadata-only 操作。公共层没有 prepare/commit/rollback 状态机,不能保留针对已删除协议的 fault backend。
| 故障点 | 成功标准 |
|---|---|
| 重叠 map | unmap_overlap = false 返回 AlreadyExists,旧区域不变 |
| area split/shrink | 地址、actual/reported flags 和 backend offset 一致 |
| metadata-only move | 不调用 backend,不重复释放物理页 |
| 12 GiB Linear map | 不建立与 3,145,728 个基础页成比例的软件快照 |
| ArceOS 分配型 map 中间失败 | populate_pages() 回滚当前操作已安装的全部前缀页,并归还当前尚未映射及已经解除映射的 frame |
| Axvisor 分配型 map 中间失败 | 固定当前遗留语义:axaddrspace 尚不回滚已安装前缀,失败页的 frame 也可能泄漏;测试应准确暴露该差异 |
| 分配型 unmap | 每个被删除的 allocation-backed frame 恰好释放一次;axaddrspace 遇到大页首项时先删项后失败的行为需单独断言 |
| 跨多区域 backend 失败 | 明确验证直接语义,不声称公共层自动恢复前缀 |
ArceOS、Starry 和 axaddrspace backend 各自增加 frame、常驻内存集大小/写时复制或 Guest RAM ownership 断言。Starry 的 clone、连续填页和 mremap 使用专用恢复测试,不把 Linux 策略下沉为所有消费者共同承担的状态机。
3. Starry、DMA 与 MMIO 测试
Starry、DMA 和内存映射输入 输出都涉及跨对象生命周期,必须覆盖 close、unmap、fork、timeout 和驱动初始化失败等非正常顺序。
3.1 Starry 虚拟内存
Starry 测试要同时比较用户可见结果和内部计数。涉及 syscall 语义时,测试期望以 Linux/POSIX 行为为基准。
| 用例 | 断言 |
|---|---|
| RLIMIT_AS replacement | 只计算 retained + requested,超限不改虚拟内存区域 |
| overcommit proc 展示 | 当前 procfs 展示 overcommit 控制项,/proc/meminfo 的 Committed_AS 仍为 0 |
| fork 写时复制 overflow | 当前 u8 引用计数达到上限时不能静默回绕 |
| fork 中间失败 | parent flags/refs/常驻内存集大小与 child resources回滚 |
| private file read→write | 常驻内存集大小 File 迁移到 Anon |
| mremap move | 只移动页表项;物理页所有权和聚合计数保持不变 |
| allocator page-cache reclaim | page allocation 失败后锁外调用注册 reclaim,最多 4 轮重试 |
| proc status/statm | 虚拟内存大小、常驻内存集大小 categories、peak和 stack分类一致 |
Starry 直接发现的 QEMU/board case 应覆盖多线程 fault、fork/exec/exit、memfd/shared mapping、MAP_FIXED 和跨虚拟内存区域 mprotect。重型内存压力负载放在 apps/starry,通过 cargo xtask starry app 执行。