
裸机 Rust 中的 buddy_system_allocator用 LockedHeap 提供全局堆用 FrameAllocator 分配 MMIO 地址空间【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rustbuddy_system_allocator是 Rust 生态中一个经典且轻量的伙伴系统buddy system内存分配器 crate在本仓库的裸机bare-metal课程中用于解决两类核心问题一类是通过LockedHeap实现GlobalAlloc让no_std环境也能安全地使用标准alloccrate 提供的Vec、String等堆上类型另一类是通过FrameAllocator分配普通堆以外的地址空间例如为 PCI BAR 预留 MMIO 区域。读完本文你将掌握这两个组件的完整用法、仓库内的可运行示例以及配套的构建与运行方式并理解伙伴系统分配器在无操作系统环境下的适用边界。为什么裸机环境需要一个分配器在#![no_std]的裸机程序中没有操作系统提供堆管理alloccrate 中的Vec、Box、String等类型都无法直接使用。要启用它们必须先实现一个全局堆分配器global heap allocator也就是实现GlobalAlloctrait并用#[global_allocator]属性把它注册到二进制文件中。实现方式有很多可以自己写、接入现有的分配器也可以直接使用社区成熟的 crate。本仓库课程见 alloc 章节推荐的就是buddy_system_allocator——一个实现基本伙伴系统分配器的 crate。它的价值体现在两个方面作为全局堆分配器通过LockedHeap实现GlobalAlloc让标准alloccrate 正常工作作为通用地址空间分配器通过FrameAllocator分配任意地址空间例如为 PCI BAR 划分 MMIO 区域。从仓库源码看课程在src/bare-metal/alloc-example/Cargo.toml与src/bare-metal/useful-crates/allocator-example/Cargo.toml两处都引入了buddy_system_allocator 0.13.0即课程实际使用的版本为 0.13.x下文示例均以该版本为准。方式一用 LockedHeap 实现全局堆分配器先看全局堆的场景。课程在 alloc-example 示例 中给出了一个完整、可直接编译的no_std堆分配器实现#![no_main] #![no_std] extern crate alloc; extern crate panic_halt as _; use alloc::string::ToString; use alloc::vec::Vec; use buddy_system_allocator::LockedHeap; #[global_allocator] static HEAP_ALLOCATOR: LockedHeap32 LockedHeap::32::new(); const HEAP_SIZE: usize 65536; static mut HEAP: [u8; HEAP_SIZE] [0; HEAP_SIZE]; pub fn entry() { // SAFETY: HEAP is only used here and entry is only called once. unsafe { // Give the allocator some memory to allocate. HEAP_ALLOCATOR.lock().init(raw mut HEAP as usize, HEAP_SIZE); } // Now we can do things that require heap allocation. let mut v Vec::new(); v.push(A string.to_string()); }这段代码拆解开来包含四个关键步骤声明分配器并注册为全局分配器static HEAP_ALLOCATOR: LockedHeap32 LockedHeap::32::new();创建了伙伴系统分配器实例#[global_allocator]属性让它成为整个二进制唯一的堆分配器。预留静态堆内存static mut HEAP: [u8; 65536]在数据段中预留 64 KiB 的堆空间。伙伴系统分配器本身不拥有内存它需要调用方在初始化时把一段连续内存“交”给它。初始化在entry()中通过HEAP_ALLOCATOR.lock().init(heap_start, heap_size)把HEAP的起始地址与大小交给分配器。注意init需要unsafe因为HEAP是static mut且课程源码注释明确约定“HEAP只在这里使用、entry只被调用一次”。之后即可正常使用堆类型Vec::new()、A string.to_string()等操作会透明地走LockedHeap分配与释放。理解 LockedHeap 的 const 参数最大 orderLockedHeap32中的32是编译期常量参数表示分配器的最大 order阶即伙伴系统允许合并的最大内存块阶数。课程在 alloc.md 的细节说明中给出了量化结论The const parameter ofLockedHeapis the max order of the allocator; i.e. in this case it can allocate regions of up to 2**32 bytes.也就是说LockedHeap32单次最多可以分配 2³² 字节4 GiB的区域。实际的伙伴系统逻辑是分配请求向上取整到 2 的幂次大小在对应的阶上查找空闲块若没有则从更高阶块逐级拆分释放时则把相邻的空闲伙伴块逐级合并回去。max order决定了这套“拆分-合并”机制的上限也决定了内部空闲链表数组的长度因此需要根据目标平台的内存规模合理选择。使用 LockedHeap 时的注意事项全局分配器必须唯一如果依赖树中任何一个 crate 依赖了alloc那么二进制文件中就必须且只能有一个全局分配器通常定义在最顶层的 binary crate 中。若重复定义链接阶段会直接报错。panic handler 必须存在extern crate panic_halt as _;的作用是强制链接panic_haltcrate为no_std程序提供 panic handler否则链接器会因缺少 panic 处理符号而失败。构建与运行前提课程明确说明这个示例“可以编译但无法运行”因为它没有真正的入口点entry只是普通函数没有调用它的启动代码要实际跑起来需要在真实硬件或模拟器如 QEMU中配合启动代码使用。其他替代方案伙伴系统分配器不是唯一选择你也可以编写自己的分配器或接入已有的分配器buddy_system_allocator的价值在于开箱即用且代码量小适合课程演示与教学。方式二用 FrameAllocator 分配堆之外的地址空间buddy_system_allocator的第二类用途是不把它当作GlobalAlloc而是当作一个通用的地址空间分配器。这正是本仓库 useful-crates 章节 的核心示例场景为 PCI BAR 分配 MMIO 空间。PCI 设备通过基地址寄存器Base Address RegisterBAR向系统声明其需要映射的 MMIO 区域。驱动在初始化时需要为每个 BAR 找到一个尚未被占用的、大小和对齐都满足要求的地址区间。这类需求并不适合走普通堆而更适合由专门的地址空间分配器来管理。课程给出的示例完整源码见 allocator-example/src/main.rs如下use buddy_system_allocator::FrameAllocator; use core::alloc::Layout; fn main() { let mut allocator FrameAllocator::32::new(); allocator.add_frame(0x200_0000, 0x400_0000); let layout Layout::from_size_align(0x100, 0x100).unwrap(); let bar allocator .alloc_aligned(layout) .expect(Failed to allocate 0x100 byte MMIO region); println!(Allocated 0x100 byte MMIO region at {:#x}, bar); }逐行拆解 FrameAllocator 示例创建分配器FrameAllocator::32::new()创建一个初始不持有任何内存的伙伴系统分配器。与LockedHeap一样32同样是最大 order决定可管理地址空间的上界。登记地址区间allocator.add_frame(0x200_0000, 0x400_0000)把0x200_0000到0x400_0000这段地址空间加入空闲列表区间为左闭右开[0x200_0000, 0x400_0000)共 32 MiB。这一步等价于告诉分配器“这些地址是空闲可用的”。构造分配布局Layout::from_size_align(0x100, 0x100)描述一次分配请求大小为 0x100 字节、对齐要求也是 0x100 字节。Layout类型来自core::alloc与GlobalAlloc::alloc的请求描述方式完全一致。分配并对齐allocator.alloc_aligned(layout)从登记的空闲区间中切出一块满足大小与对齐要求的子区间返回起始地址示例中是usize。失败时返回错误示例用expect直接断言成功。输出结果println!(Allocated 0x100 byte MMIO region at {:#x}, bar)以十六进制打印分配到的地址方便在真实硬件上核对 BAR 映射值。为什么这里用“对齐等于大小”课程在该示例的说明中特别强调了一个硬件事实PCI BARs always have alignment equal to their size.PCI 规范要求 BAR 的地址对齐等于其大小一个 0x100 字节的 BAR 必须 0x100 对齐一个 1 MiB 的 BAR 必须 1 MiB 对齐。正因为如此示例中构造Layout时把size与align都设为 0x100 是符合 PCI 语义的。而伙伴系统分配器的天然特性恰好满足这一要求——它分配的块总是 2 的幂大小且按同阶对齐因此用于 BAR 分配非常契合。如果硬件的对齐规则更复杂alloc_aligned也能接受任意的Layout只需按设备手册正确构造即可。运行方式与限制课程给出了明确的运行指引在src/bare-metal/useful-crates/allocator-example/目录下执行cargo run即可运行该示例不能在 Rust Playground 中运行因为示例依赖buddy_system_allocator这个外部 cratePlayground 无法解析该依赖。此外仓库还为该示例提供了 Bazel 构建配置见 BUILD.bazel通过rust_binary构建allocator-example可执行文件依赖由all_crate_deps(normal True)从 Bazel 管理的 crate 仓库统一解析同时定义了一个rust_testallocator-example_test验证构建产物。如果你在仓库环境中使用 Bazel 而非 Cargo可以直接复用这套配置。两个组件的关系与选型总结LockedHeap与FrameAllocator是同一个伙伴系统核心的两种对外形态可用下表对比维度LockedHeapFrameAllocator主要用途实现GlobalAlloc支撑alloccrate分配任意地址空间如 MMIO、帧使用前提#[global_allocator]注册需先init堆内存add_frame登记空闲地址区间是否加锁内部自带锁lock()访问需要调用方自行保证独占示例中为局部变量典型场景no_std程序的Vec/String/BoxPCI BAR 的 MMIO 地址分配版本依赖buddy_system_allocator 0.13.0课程仓库实际版本同左选型时遵循一条主线只要你想让堆分配类型可用就走LockedHeap#[global_allocator]只要你想管理堆之外的地址资源就走FrameAllocatoradd_framealloc_aligned。两者共享相同的伙伴系统拆分/合并算法因此内存使用模式2 的幂块、伙伴合并也一致。在课程中的定位与延伸阅读本文讨论的buddy_system_allocator是裸机课程“实用 crate”专题的一部分该专题还介绍了页表管理 crateaarch64-paging见 aarch64-paging 章节两者结合起来可以覆盖裸机系统中“内存堆管理 虚拟地址空间管理”两条主线。相关章节在课程目录SUMMARY.md中归属于bare-metal模块属于学员掌握no_std开发后的进阶内容。如果你需要完整的裸机分配方案可以进一步阅读alloc 章节全局堆分配器的原理与使用约束唯一性、panic handler、max order 语义alloc-example 完整源码LockedHeap全局分配器的完整no_std实现allocator-example 完整源码FrameAllocator分配 MMIO 空间的完整示例allocator-example 的 Cargo.toml 与 Bazel 配置两种构建方式的实际依赖声明。掌握这两个入口之后你便可以在自己的裸机项目中自由地组合使用伙伴系统分配器无论是为内核堆供能还是为设备驱动预留地址空间。【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考