VM 列表管理
本节介绍 AxVisor 如何管理虚拟机列表,包括虚拟机的注册、查询、移除,以及与其他模块的协作关系。
模块交互关系
vm_list 模块是 VMM 的核心数据管理模块,负责维护系统中所有虚拟机实例的全局注册表。它采用集中式管理的设计模式,为其他模块提供统一的 VM 访问接口。
vm_list 与多个模块交互,形成了一个清晰的分层架构。下图展示了 vm_list 在整个系统中的中心地位,以及它如何与上层的 Shell 命令和下层的 VMM 模块进行交互:
交互关系解析:
- config → vm_list:VM 创建完成后,config 模块将其注册到 vm_list,建立全局可见性
- shell → vm_list:Shell 命令通过查询接口访问 VM 信息,实现用户交互
- vcpus ⇄ vm_list:Vcpu 模块通过弱引用访问 VM,避免循环引用导致内存泄漏
- 辅助函数 → vm_list:mod.rs 提供的封装函数简化了 VM 访问模式
- vm_list → VM 实例:vm_list 持有所有 VM 的强引用,管理它们的生命周期
从架构图可以看出,vm_list 处于整个系统的中心位置,上层的 Shell 命令通过它来查询和管理 VM,而底层的 config 模块在创建 VM 后也需要将其注册到这里。
模块职责
下表详细说明了各个模块与 vm_list 的交互方式和设计考量:
| 模块 | 与 vm_list 的交互 | 说明 |
|---|---|---|
| config | push_vm | 创建 VM 后注册到列表 |
| vcpus | 通过 Weak<VM> 引用 | 避免循环引用,需要时升级为 Arc |
| shell 命令 | get_vm_list, get_vm_by_id | 查询 VM 信息和状态 |
| mod.rs | with_vm, with_vm_and_vcpu | 提供安全访问 VM 的辅助函数 |
| vm delete | remove_vm | 删除 VM 时从列表移除 |
config 模块的职责是根据配置文件初始化虚拟机实例。当一个 VM 对象创建完成后,config 模块会调用 push_vm 将其注册到全局列表中。这个设计遵循了"创建者负责注册"的原则,确保每个成功创建的 VM 都能被系统追踪。
vcpus 模块与 VM 之间存在特殊的引用关系。每个 Vcpu 任务需要访问其所属的 VM,但如果直接持有 Arc<VM> 强引用,会形成循环引用(VM 拥有 Vcpu,Vcpu 又引用 VM),导致内存永远无法释放。因此 vcpus 模块使用 Weak<VM> 弱引用,在需要访问 VM 时通过 upgrade() 方法临时提升为 Arc<VM>,使用完毕后自动释放。这是 Rust 中处理循环引用的标准模式。
shell 命令层通过查询接口与 vm_list 交互。vm list 命令调用 get_vm_list() 获取所有 VM 的列表,vm show 和 vm delete 命令则通过 get_vm_by_id() 获取特定 VM。这些接口返回的都是 Arc<VM> 的克隆,增加引用计数但不复制 VM 对象本身,既保证了数据安全,又避免了不必要的开销。
mod.rs 辅助函数提供了更高级的封装,如 with_vm 和 with_vm_and_vcpu,这些函数采用闭包模式,自动处理 VM 的获取、使用和释放,减少了样板代码,提高了代码的可读性和安全性。
数据结构
vm_list 模块的数据结构设计体现了 Rust 的类型安全和内存安全特性,通过类型别名、智能指针和互斥锁的组合,构建了一个线程安全且高效的 VM 管理系统。
VMList 结构
VMList 是一个 简单但功能完整的容器,它封装了 BTreeMap 来存储虚拟机实例:
核心设计要点:
-
全局静态变量 GLOBAL_VM_LIST:使用
lazy_static或once_cell初始化的全局变量,确保在整个程序运行期间只有一个 VMList 实例。这是单例模式在 Rust 中的实现方式。 -
Mutex 保护并发访问:所有对 VMList 的操作都必须先获取 Mutex 锁。使用
spin::Mutex而非标准库的std::sync::Mutex,因为 AxVisor 运行在 no_std 环境中,需要自旋锁而非依赖操作系统的线程调度。 -
返回 Option 类型:查询和移除操作返回
Option<VMRef>,明确表达了"VM 可能不存在"的语义,强制调用者处理这种情况,避免了空指针异常。
类型定义
VM 相关类型在 vmm/mod.rs 中定义,这些类型别名简化了代码并提供了更好的语义:
类型别名的设计意图:
-
VM:是
AxVM<AxVMHalImpl, AxVCpuHalImpl>的别名,隐藏了泛型参数的复杂性。AxVM 是虚拟机的核心实现,两个泛型参数分别是 VM 层和 Vcpu 层的硬件抽象层(HAL)实现。 -
VMRef:等价于
Arc<VM>,表示一个虚拟机的共享引用。使用 Arc(原子引用计数)允许多个所有者同时持有同一个 VM 的引用,当最后一个引用被释放时,VM 会自动清理。 -
VCpuRef:等价于
Arc<VCpu>,表示一个虚拟 CPU 的共享引用。每个 VM 包含一个Vec<VCpuRef>,存储其所有的虚拟 CPU。
所有权关系:
- VMList 持有
VMRef(强引用),是 VM 的主要所有者 - Vcpu 任务持有
Weak<VM>(弱引用),避免循环引用 - Shell 命令和辅助函数获取
VMRef的临时克隆,使用完毕后自动释放 - VM 持有
Vec<VCpuRef>(强引用),拥有其所有 Vcpu
全局 API
vm_list 模块提供了三个核心 API 用于管理虚拟机的生命周期。这些 API 都是线程安全的,可以从多个线程或 CPU 核心同时调用。
添加虚拟机
push_vm(vm) 函数用于将新创建的虚拟机注册到全局列表中,这是每个 VM 生命周期的起点。
使用示例:
// 在 config 模块中
let vm = AxVM::new(config)?;
push_vm(vm); // 所有权转移到全局列表
查询虚拟机
vm_list 提供了两种查询方式:按 ID 查询单个 VM,或获取所有 VM 的列表。
按 ID 查询
get_vm_by_id(vm_id) 返回指定 ID 的虚拟机引用: