starry-vm
路径:
components/starry-vm类型:库 crate 分层:组件层 / StarryOS 用户虚拟内存访问组件 版本:0.3.0文档依据:Cargo.toml、src/lib.rs、src/thin.rs、src/alloc.rs、tests/test.rs、os/StarryOS/kernel/src/mm/access.rs、os/StarryOS/kernel/src/syscall/task/execve.rs、os/StarryOS/kernel/src/syscall/task/clone3.rs、os/StarryOS/kernel/src/mm/io.rs、os/StarryOS/kernel/src/syscall/ipc/msg.rs
starry-vm 是 StarryOS 的“用户虚拟内存访问层”。它定义了一套与具体地址空间实现解耦的读写接口,使 syscall 实现可以安全地把用户态指针解释成 Rust 值、切片和字符串,而不必把用户态地址直接当普通内核指针使用。
名字里虽然带 vm,但它并不管理页表、VMA、mmap、ELF 装载或缺页策略。那些真正的“虚拟内存管理”职责在 starry-kernel::mm,尤其是 AddrSpace、loader.rs 和 access.rs 中。
架构设计
设计定位
从 StarryOS 的真实调用关系看,starry-vm 位于 syscall 层和用户地址空间之间:
- syscall 参数进入内核后,常先通过
VmPtr/VmMutPtr/vm_load*()做指针编组。 - 真正的地址检查、
user_copy和缺页协作由消费者实现VmIo。 - 同一套 API 既能被内核实现使用,也能在 host 侧测试里用假内存池替身实现。
这使它更像“用户内存 I/O 抽象层”,而不是“地址空间对象模型”。
模块结构
src/lib.rs:定义VmError、VmResult、VmIo、vm_read_slice()、vm_write_slice()。src/thin.rs:定义VmPtr/VmMutPtr两个轻量指针 trait,支持原始指针和NonNull<T>。src/alloc.rs:在启用allocfeature 时提供vm_load_any()、vm_load()、vm_load_until_nul()。
1.3 核心抽象
VmIo:唯一需要由外部实现的 unsafe trait,提供new()、read()、write()。VmError:只暴露三类错误,BadAddress、AccessDenied、TooLong。VmPtr:提供nullable()、vm_read_uninit()、vm_read()。VmMutPtr:在VmPtr基础上增加vm_write()。vm_read_slice()/vm_write_slice():面向切片的批量拷贝入口。vm_load_until_nul():按块扫描 C 风格 NUL 终止数组,并限制总长度上限。
其中有两个很重要的实现特征:
- 所有入口都会先检查对齐要求,未对齐直接返回
VmError::BadAddress。 vm_load_until_nul()的最大扫描字节数是131072,也就是 128 KiB,避免对坏指针进行无界扫描。
1.4 VmIo 的外部实现机制
starry-vm 最关键的设计点,是用 #[extern_trait(VmImpl)] 把“当前环境下的实际 VM 访问后端”留给外部实现。
在 StarryOS 内核中,这个实现位于 os/StarryOS/kernel/src/mm/access.rs:
struct Vm(IrqSave)作为具体实现体,读写期间持有中断保护。check_access()先检查地址是否位于用户空间窗口内。access_user_memory()标记当前线程处于用户内存访问区间,允许 page fault 在内核中安全发生。VmIo::read()/write()通过ax-hal::asm::user_copy()真正完成跨地址空间拷贝。- 注册的 page fault handler 再转给
AddrSpace::handle_page_fault()补页。
也就是说,starry-vm 自身不知道“当前地址空间”是什么,但 StarryOS 通过外部实现把它和当前线程的 AddrSpace 绑定了起来。
1.5 真实调用主线
在 StarryOS 里,它的使用面非常广:
这说明它承担的是“用户态参数与缓冲区编组”这一整段,而不是个别 syscall 的附属工具。
1.6 与地址空间管理的边界
下面这些职责都不在 starry-vm 内部:
AddrSpace的创建、克隆和销毁。mmap/brk/mprotect/mincore等地址空间布局调整。- ELF 装载、用户栈和堆映射。
- 文件后端、COW、线性映射、缺页策略。
这些职责在 starry-kernel::mm::{aspace,loader,access}。starry-vm 只负责“已经给你一个用户地址,现在安全地把它读出来或写回去”。
核心功能
功能概览
- 对用户态原始指针进行对齐检查和安全读写。
- 把用户空间数组加载为内核侧
Vec<T>。 - 读取 NUL 终止数组,为
argv、envp、路径字符串等场景服务。 - 为信号栈 frame、futex 地址、时间结构体、消息队列缓冲区等提供统一编组接口。
2.2 StarryOS 中的关键使用点
syscall/task/execve.rs:用vm_load_until_nul()读取argv/envp指针数组。mm/access.rs:实现VmIo并扩展出VmBytes/VmBytesMut等 I/O 视图。syscall/task/clone3.rs:用vm_read_slice()读取clone_args。syscall/ipc/msg.rs:用vm_load()/vm_write_slice()复制消息正文。syscall/mm/mmap.rs:在mremap风格路径中复制用户数据。task/ops.rs、syscall/sync/futex.rs:通过VmPtr/VmMutPtr读写 futex 相关用户地址。starry-signal:用VmMutPtr::vm_write()把SignalFrame压入用户栈。
2.3 关键 API 使用示例
典型使用方式如下:
let head = head_ptr.vm_read()?;
let argv = vm_load_until_nul(argv_ptr)?;
user_ptr.vm_write(value)?;
let data = vm_load(buf_ptr, len)?;
vm_write_slice(out_ptr, &data)?;