ax-mm
路径:
os/arceos/modules/axmm类型:库 crate 分层:ArceOS 层 / ArceOS 内核模块 版本:0.3.0-preview.3文档依据:Cargo.toml、src/lib.rs、src/aspace.rs、src/backend/mod.rs、src/backend/linear.rs、src/backend/alloc.rs
ax-mm 是 ArceOS 的虚拟内存管理模块。它把 ax-hal::paging::PageTable 提供的页表能力、ax-memory-set 提供的区间元数据管理,以及 ax-alloc 提供的物理页框分配整合成统一的地址空间抽象 AddrSpace,负责建立和维护宿主内核自己的虚拟地址空间。
架构设计
设计定位
ax-mm 的职责边界非常明确:
- 它管理的是 宿主内核地址空间,核心对象是
AddrSpace和全局KERNEL_ASPACE。 - 它不直接负责底层页表项格式、TLB 指令或地址翻译细节,这些由
ax-hal提供。 - 它也不直接承担 StarryOS 进程级复杂地址空间策略,例如 COW、文件后端和用户态高级内存语义;这些通常在 StarryOS 自己的
mm层扩展。
因此,ax-mm 更像是 ArceOS 宿主侧的“页表驱动型地址空间管理器”,而不是所有项目共享的一切内存策略中心。
模块结构
src/lib.rs:对外 API 入口。负责全局KERNEL_ASPACE、init_memory_management()、iomap()、new_user_aspace()以及MemRegionFlags到MappingFlags的转换。src/aspace.rs:核心类型AddrSpace所在文件,封装映射、取消映射、权限更新、跨地址空间读写和缺页处理逻辑。src/backend/mod.rs:定义Backend枚举,并把Linear与Alloc两类映射后端统一到ax_memory_set::MappingBackend契约上。src/backend/linear.rs:线性映射实现,适用于虚拟地址与物理地址存在固定偏移的场景。src/backend/alloc.rs:分配式映射实现,支持 eager populate 和按需缺页分配两种模式。
1.3 关键数据结构与全局对象
AddrSpace:ax-mm的核心对象,内部持有虚拟地址范围、MemorySet<Backend>和PageTable。Backend:两类映射后端的统一抽象。Linear { pa_va_offset }:基于固定偏移的线性映射。Alloc { populate }:基于物理页框分配的映射,可按需填充。
KERNEL_ASPACE:全局内核地址空间单例,通过LazyInit<SpinNoIrq<AddrSpace>>保存。PageTable:由ax-hal::paging暴露的多架构页表类型,是AddrSpace实际修改硬件映射的核心对象。
1.4 内核地址空间初始化主线
内核页表安装路径集中在 init_memory_management():
其逻辑可概括为:
- 根据
ax-hal::mem::kernel_aspace()创建一个空的AddrSpace。 - 遍历
ax-hal::mem::memory_regions()暴露的物理内存、MMIO 和其他区间。 - 把
MemRegionFlags转为MappingFlags后,用map_linear()把这些区间映射到内核虚拟地址。 - 将构造好的地址空间安装到
KERNEL_ASPACE。 - 最后用
ax-hal::asm::write_kernel_page_table()和flush_tlb()把新根页表写入硬件。
从核路径 init_memory_management_secondary() 则明显更轻,只负责再次写入同一根页表并 flush TLB,而不会重建整套地址空间元数据。
1.5 映射模型与缺页主线
ax-mm 提供两种典型映射模式:
- 线性映射:通过固定
pa_va_offset计算物理地址,适合内核镜像区、直映区和大部分平台内存映射。 - 分配式映射:通过
ax-alloc::global_allocator()分配物理页框,可选择:populate = true:映射创建时立即分配并建立页表项。populate = false:先建立“占位”区域,等实际访问触发缺页后,再 在handle_page_fault()中补页并 remap。
因此,ax-mm 的缺页处理并不是通用“用户态缺页异常框架”,而是主要服务于它自己 Alloc backend 的 lazy population 机制。
1.6 架构与 feature 差异
copyfeature:启用new_user_aspace()与copy_mappings_from(),允许在用户地址空间中复制一段内核映射。- 架构分支:在 aarch64 和 loongarch64 上,源码明确不复制内核映射到用户页表;在其他架构上才执行复制逻辑。
iomap():对设备物理地址执行线性映射,并在重复映射时以AlreadyExists为特例继续执行权限修正。
核心功能
功能概览
- 创建并安装全局内核地址空间。
- 提供
map_linear()、map_alloc()、unmap()、protect()等地址空间操作。 - 提供
iomap()用于设备 MMIO 映射。 - 在
copyfeature 下提供new_user_aspace()和跨页表复制逻辑。 - 提供
read()/write()等跨地址空间内存访问辅助接口。
使用场景
init_memory_management():由运行时在开启paging的 bring-up 主线中调用。kernel_aspace():供宿主内核后续查询和操作全局页表。iomap(pa, size, flags):供设备驱动或平台 glue 把物理 MMIO 区映射到内核虚拟地址。AddrSpace::map_linear():适合固定偏移映射。AddrSpace::map_alloc():适合需要独立分配页框的虚拟区。AddrSpace::handle_page_fault():主要服务Allocbackend 的 lazy mapping。
使用方式
最常见的外部接入方式不是手动新建很多 AddrSpace,而是使用全局内核地址空间:
let mut aspace = ax-mm::kernel_aspace().lock();
let va = ax-mm::iomap(mmio_paddr, mmio_size, flags)?;
aspace.protect(va.into(), mmio_size, flags)?;
这条调用链在 DMA、设备驱动和平台相关路径中都很常见。
依赖关系
直接依赖
ax-hal:提供页表类型、地址转换、内核地址空间布局和写根页表指令。ax-memory-set:保存虚拟区间元数据,并通过 backend trait 协作执行映射。ax-alloc:为Allocbackend 提供物理页框来源。memory_addr、ax-errno、ax-kspin、ax-lazyinit:分别提供地址类型、错误、锁和全局单例初始化。
主要消费者
ax-runtime:在paging路径中调用init_memory_management()和init_memory_management_secondary()。ax-api:在pagingfeature 下对外再导出ax-mm。ax-dma:通过kernel_aspace().lock().protect(...)调整 DMA 映射权限。- StarryOS:虽然用户地址空间实现主要在
starry-kernel自己的mm中,但仍会复用kernel_aspace()提供的宿主内核映射视图。
3.3 间接消费者
- 启用分页的 ArceOS 样例与测试。
- 通过
ax-std、ax-api或ax-runtime间接使用宿主页表栈的上层项目。 - Axvisor 的宿主内核路径,但不包含 guest 二级页表策略本身。
开发指南
接入方式
[dependencies]
ax-mm = { workspace = true }
若需要用户地址空间复制能力,还应在消费方显式启用 copy feature。
4.2 初始化与改动约束
init_memory_management()应始终视为运行时启动链的一部分,不能在系统运行中重复调用。- 修改
new_kernel_aspace()时,要同步核对ax-hal::mem::memory_regions()暴露的区域语义是否仍然匹配。 - 修改
Allocbackend 时,要同时验证 eager populate 与 lazy fault-in 两条路径。 - 修改
copy路径时,要区分 aarch64 / loongarch64 与其他架构在“是否复制内核映射”上的差异。
4.3 关键开发建议
- 设备映射优先走
iomap(),不要在外部重复实现一套物理到虚拟映射逻辑。 - 任何页表安装逻辑都应最终通过
ax-hal::asm::write_kernel_page_table()与flush_tlb()完成。 - 若在上层实现更复杂的用户地址空间策略,应把
ax-mm明确当作“宿主内核页表基础设施”,而不是强行把所有高层策略塞回本 crate。