
CubeSandbox Hypervisor virtio-balloon 深度指南参数配置、内存回收原理与运行时弹性调整【免费下载链接】CubeSandboxInstant, Concurrent, Secure Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox本文以 CubeSandbox 仓库中 hypervisor/docs/balloon.md 为骨架结合 virtio-balloon 设备源码、参数解析实现 与 运行时调整 API 展开系统讲解该 Cloud Hypervisor 分支中 virtio-balloon 设备的完整配置项、底层内存回收机制与动态调整方法。读完本文你将掌握--balloon三个参数size、deflate_on_oom、free_page_reporting的语义与取值规则理解 inflate/deflate/free-page-reporting 三条 virtqueue 背后的madvise/fallocate实现并学会通过 HTTP API 在运行时安全地调整 balloon 大小。一、virtio-balloon 在 CubeSandbox Hypervisor 中的定位CubeSandbox 的 hypervisor 目录基于 Cloud Hypervisor 实现其中 balloon 设备严格遵循 VIRTIO 规范实现。它的核心价值在于为主机Host提供一条回收客户机Guest内存的途径——通过控制 Guest 可见内存的数量让宿主机能够在不关闭虚拟机的前提下取回空闲内存。除此之外它还为 Guest 内存管理提供了若干实用特性如 OOM 时自动放气、空闲页上报。与传统的静态内存分配相比balloon 设备的意义在于“可变性”VM 的总内存RAM与 balloon 占用的内存之差才是 Guest 实际可用的内存而 balloon 的大小既可以在启动时指定也可以在运行时通过管理 API 调整从而在“多租户资源超卖”与“单租户突发内存需求”之间取得平衡。二、BalloonConfig 参数总览BalloonConfig在命令行视角下即--balloon选项它包含三个参数。官方文档给出如下 Rust 结构体定义见 hypervisor/docs/balloon.mdstruct BalloonConfig { pub size: u64, pub deflate_on_oom: bool, pub free_page_reporting: bool, }该结构体在仓库源码中亦有对应定义位于 hypervisor/vmm/src/vm_config.rs#L381-L390#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct BalloonConfig { pub size: u64, /// Option to deflate the balloon in case the guest is out of memory. #[serde(default)] pub deflate_on_oom: bool, /// Option to enable free page reporting from the guest. #[serde(default)] pub free_page_reporting: bool, }两个布尔字段均带#[serde(default)]与“可选参数、默认关闭”的语义一致。从 CLI 角度看命令行为--balloon balloon Balloon parameters sizeballoon_size,deflate_on_oomon|off,free_page_reportingon|off该语法定义同样可以在 hypervisor/vmm/src/config.rs#L1350-L1353 的BalloonConfig::SYNTAX常量中看到CLI 参数注册位于 hypervisor/src/main.rs#L222-L223。参数汇总如下表参数类型是否必填默认值作用size64 位无符号整数字节必填—balloon 设备大小从 VM 总内存中扣除deflate_on_oom布尔on/off可选off允许 Guest 在 OOM 时放气缩小 balloon自救free_page_reporting布尔on/off可选off允许 Guest 上报已释放的空闲页供 VMM 通知宿主回收参数解析实现细节参数解析由 hypervisor/vmm/src/config.rs#L1355-L1386 的BalloonConfig::parse完成可以佐证上文表格中的默认值语义pub fn parse(balloon: str) - ResultSelf { let mut parser OptionParser::new(); parser.add(size); parser.add(deflate_on_oom); parser.add(free_page_reporting); parser.parse(balloon).map_err(Error::ParseBalloon)?; let size parser .convert::ByteSized(size) .map_err(Error::ParseBalloon)? .map(|v| v.0) .unwrap_or(0); let deflate_on_oom parser .convert::Toggle(deflate_on_oom) .map_err(Error::ParseBalloon)? .unwrap_or(Toggle(false)) .0; let free_page_reporting parser .convert::Toggle(free_page_reporting) .map_err(Error::ParseBalloon)? .unwrap_or(Toggle(false)) .0; Ok(BalloonConfig { size, deflate_on_oom, free_page_reporting, }) }值得注意的实现细节size使用ByteSized类型解析因此命令行中可以直接书写1G、512M等带单位的值而结构体内部以字节u64存储deflate_on_oom与free_page_reporting使用Toggle类型解析接受on/off缺省时回退为falsesize未指定时回退为0但在启动配置校验阶段balloon 大小不允许大于等于 RAM 大小见下文“配置校验”一节并且 balloon 设备通常需要显式指定size。三、三个参数逐一详解3.1sizeballoon 设备的初始大小size表示 balloon 设备的大小它会被从 VM 的总内存中扣除。举例来说如果创建一个 4GiB RAM 的 VM同时配置 1GiB 的 balloon那么 Guest 实际可访问的内存为 3GiB。需要强调一个容易误解的点Guest 在启动时看到的是全部 RAM即 4GiB除非 Guest 内运行了具备 balloon 感知能力balloon enlightened的驱动/代理否则它有权使用全部内存。balloon 的职责正是通过这种“可见但被扣减”的机制促使 Guest 主动把内存交还给宿主机。该参数必填取值为 64 位无符号整数单位是字节。示例--balloon size1G从设备初始化源码看hypervisor/virtio-devices/src/balloon.rs#L351-L402size会被右移 12 位即除以 4KiB 页大小对应常量VIRTIO_BALLOON_PFN_SHIFT 12转换为num_pages写入 virtio 配置空间让 Guest 驱动知道宿主期望它交出多少页let config VirtioBalloonConfig { num_pages: (size VIRTIO_BALLOON_PFN_SHIFT) as u32, ..Default::default() };3.2deflate_on_oomGuest 内存不足时的自动放气deflate_on_oom允许 Guest 在自身发生 Out Of MemoryOOM时缩小放气balloon。只要 balloon 当前大小大于 0Guest 就可以在 OOM 恢复需要时将 balloon 一路缩小到 0从而取回被扣减的内存。该参数可选取值为布尔类型默认off。示例--balloon size2G,deflate_on_oomon从源码层面看该选项对应 virtio-balloon 协议中的特性位VIRTIO_BALLOON_F_DEFLATE_ON_OOM值 2见 hypervisor/virtio-devices/src/balloon.rs#L52-L56。设备创建时仅在该选项开启时向 Guest 通告这一特性let mut avail_features 1u64 VIRTIO_F_VERSION_1; if deflate_on_oom { avail_features | 1u64 VIRTIO_BALLOON_F_DEFLATE_ON_OOM; } if free_page_reporting { avail_features | 1u64 VIRTIO_BALLOON_F_REPORTING; }可以推断该特性依赖 Guest 内的驱动配合——只有当 Guest 驱动协商并实现了 deflate 能力时OOM 场景下的自动放气才真正生效作为宿主侧策略建议为承载高负载、内存抖动明显的业务 VM 开启此选项。3.3free_page_reporting空闲页上报与宿主回收free_page_reporting允许 Guest 向 VMM 报告“已释放的空闲页列表”。该特性不要求 balloon 有特定大小因为它不影响 balloon 的尺寸——即使size0只要开启本选项Guest 依然可以在页面被使用过后主动告知 VMM 哪些页已经空闲。基于这些信息VMM 可以向宿主内核建议“这些页已经不再需要”从而触发宿主侧的内存回收。该参数可选取值为布尔类型默认off。示例--balloon size0,free_page_reportingon这个例子非常实用它展示了不占用任何 Guest 内存、纯靠空闲页上报实现宿主回收的轻量方案适合那些不需要固定扣减内存、但希望回收 Guest 闲置内存的场景。四、底层实现剖析三条 virtqueue 与内存回收调用链virtio-balloon 设备的完整实现位于 hypervisor/virtio-devices/src/balloon.rs。设备使用独立的 epoll 线程处理队列事件balloon.rs#L248-L262其中 inflate、deflate 两条队列各自绑定一个 eventfdfree-page-reporting 队列在启用该特性时动态注册。4.1 队列布局与大小const QUEUE_SIZE: u16 128; // inflate / deflate 队列深度 const REPORTING_QUEUE_SIZE: u16 32; // free page reporting 队列深度 const MIN_NUM_QUEUES: usize 2;默认创建 2 条队列inflate、deflate仅当free_page_reporting开启时追加第 3 条 reporting 队列balloon.rs#L360-L384let mut queue_sizes vec![QUEUE_SIZE; MIN_NUM_QUEUES]; // ... if free_page_reporting { queue_sizes.push(REPORTING_QUEUE_SIZE); }4.2 inflate充气真正把内存还给宿主Guest 驱动通过 inflate 队列索引 0把要交还的页帧号PFN发给 VMM。处理逻辑见process_queueballoon.rs#L164-L222let range_base GuestAddress((pfn as u64) VIRTIO_BALLOON_PFN_SHIFT); let range_len 1 VIRTIO_BALLOON_PFN_SHIFT; match queue_index { 0 { Self::release_memory_range(desc_chain.memory(), range_base, range_len)?; } 1 { Self::advise_memory_range( desc_chain.memory(), range_base, range_len, libc::MADV_WILLNEED, )?; } _ return Err(Error::InvalidQueueIndex(queue_index)), }inflate 路径最终调用release_memory_rangeballoon.rs#L137-L162其回收动作分两步文件后备内存若内存区域有后备文件region.file_offset()则通过fallocate64以FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE在文件对应偏移处打洞真正释放底层存储匿名/映射内存对回收区间执行madvise(..., MADV_DONTNEED)提示宿主内核这些页可以立即丢弃。4.3 deflate放气把内存归还给 Guestdeflate 队列索引 1用于 Guest 需要更多内存时请求放气。从上述代码可见deflate 对目标区间执行的是madvise(..., MADV_WILLNEED)即提示宿主“这些页即将被访问”内核据此优先保留或提前准备这些页。4.4 free page reporting空闲页上报reporting 队列索引 2的处理逻辑独立实现于process_reporting_queueballoon.rs#L224-L246。与 inflate 不同reporting 请求中描述符本身携带的是内存区间基地址 长度VMM 直接对每个区间调用release_memory_range完成回收。这也解释了为什么该特性不改变 balloon 大小——它走的是独立的队列与独立的回收语义。4.5 配置空间的num_pages与actual设备配置空间结构VirtioBalloonConfig对应 Linux 内核头文件virtio_balloon.h见 balloon.rs#L84-L98pub struct VirtioBalloonConfig { // Number of pages host wants Guest to give up. num_pages: u32, // Number of pages weve actually got in balloon. actual: u32, }num_pages宿主期望 Guest 交出的页数即上文由size换算而来actualGuest 实际已放入 balloon 的页数由 Guest 驱动写回偏移为 4 字节处常量CONFIG_ACTUAL_OFFSET。设备只允许 Guest 写入该字段其他偏移的写入会被拒绝balloon.rs#L465-L495get_actual()把actual左移 12 位还原为字节数供 VMM 查询当前实际回收量balloon.rs#L417-L419。五、运行时动态调整HTTP API 与resize流程balloon 的价值不仅体现在启动参数更在于运行时不中断 VM 即可调整大小。CubeSandbox Hypervisor 通过管理 HTTP API 的resize接口暴露该能力。VmResizeData结构体hypervisor/vmm/src/api/mod.rs#L225-L230同时支持 CPU、内存与 balloon 三项的调整#[derive(Clone, Deserialize, Serialize, Default, Debug)] pub struct VmResizeData { pub desired_vcpus: Optionu8, pub desired_ram: Optionu64, pub desired_balloon: Optionu64, }运行时调整的完整调用链为客户端向PUT /api/v1/vm.resize发送{desired_balloon: 字节数}API 定义见 hypervisor/vmm/src/api/openapi/cloud-hypervisor.yamlVMM 在 hypervisor/vmm/src/vm.rs#L1414-L1426 中调用device_manager.resize_balloon(desired_balloon)resize_balloon最终落到设备层hypervisor/vmm/src/device_manager.rs#L4434-L4440 → balloon.rs#L404-L414pub fn resize(mut self, size: u64) - Result(), Error { self.config.num_pages (size VIRTIO_BALLOON_PFN_SHIFT) as u32; if let Some(interrupt_cb) self.interrupt_cb { interrupt_cb .trigger(VirtioInterruptType::Config) .map_err(Error::FailedSignal) } else { Ok(()) } }设备更新num_pages后通过Config 中断通知 Guest 驱动驱动随即按新的目标值通过 inflate/deflate 队列执行实际的页面移交整个过程无需重启或暂停 VM。另一个值得注意的细节在 hypervisor/vmm/src/vm.rs#L1421-L1425VMM 会同步更新 VM 配置中的 balloon 大小确保 VM 重启后仍沿用最近一次调整后的值避免运行时调整在重启后丢失。六、配置校验与边界条件在启动配置阶段VMM 会对 balloon 与内存的关系做一致性校验。从 hypervisor/vmm/src/config.rs#L2612-L2627 的源码可见若balloon.size ram_size会触发BalloonLargerThanRam错误——即balloon 的大小必须小于 RAM 总大小。这与文档中“size 会被从 VM 总内存中扣除”的语义一致扣减量不可能等于或超过总量本身。此外还需留意一个实践细节deflate_on_oom与free_page_reporting是否真正生效取决于 Guest 内驱动是否协商了对应特性位。宿主机侧开启特性只是前提条件完整链路需要 Guest 内核 ≥ 相应版本并启用对应配置例如 Linux 内核的CONFIG_VIRTIO_BALLOON与 free page reporting 支持。七、典型实践场景小结场景推荐配置说明多租户超卖需要明确扣减 Guest 内存--balloon size1G固定回收 1GiBGuest 可用内存 RAM − 1GiB高负载 VM担心 OOM--balloon size2G,deflate_on_oomon平时扣减 2GiBGuest OOM 时允许自动取回纯空闲页回收不占用 Guest 内存--balloon size0,free_page_reportingon借助 reporting 队列回收 Guest 已释放页运行中按负载伸缩PUT /api/v1/vm.resize携带desired_balloon无需重启Config 中断驱动动态充放气本文所述内容均可在 CubeSandbox 仓库对应源码与文档中交叉验证官方参数说明见 hypervisor/docs/balloon.md设备实现见 hypervisor/virtio-devices/src/balloon.rs参数解析见 hypervisor/vmm/src/config.rs运行时调整逻辑见 hypervisor/vmm/src/vm.rs 与 hypervisor/vmm/src/api/mod.rs。若需了解内存管理的整体设计包括内存热插拔与 virtio-mem 等更灵活的方案可继续阅读同目录下的 hypervisor/docs/memory.md 与 hypervisor/docs/hotplug.md。【免费下载链接】CubeSandboxInstant, Concurrent, Secure Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考