裸机 Rust 中的堆内存分配实现 GlobalAlloc 与使用alloc库的完整指南comprehensive-rust 课程解析【免费下载链接】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导读在 Rust 裸机bare-metal开发中no_std环境默认不提供堆分配能力但通过实现一个全局堆分配器GlobalAlloc就可以启用alloccrate从而使用Vec、String、Box等动态内存结构。本文以 Google Android 团队 Rust 课程comprehensive-rust的 Bare Metal 模块为基础结合仓库内完整可编译的示例工程alloc-example系统讲解如何为裸机程序接入堆分配器、初始化堆内存、正确声明全局分配器并深入剖析buddy_system_allocator的LockedHeap与FrameAllocator两种用法。读完本文你将能在自己的no_std裸机项目中独立搭建可用的堆分配基础设施。为什么裸机 Rust 需要alloc在无操作系统的裸机环境中Rust 程序通常以#![no_std]形式编写此时程序只能使用core库。core提供了Option、Result、迭代器、panic!、原子类型等与运行时无关的基础能力但它不包含任何需要内存分配器支持的数据结构。comprehensive-rust 的 no_std.md 用一张表格清晰划分了三个层级的能力边界core无分配切片、str、Option/Result、Display/Debug、Iterator、panic!/assert_eq!、NonNull、Future与async/await、fence与原子类型、Duration等。alloc需要全局分配器Box、Cow、Arc、Rc、Vec、BinaryHeap、BTreeMap、LinkedList、VecDeque、String、CString、format!。std依赖操作系统HashMap依赖 RNG、Mutex/Condvar等同步原语、文件系统、网络、线程、进程、时钟等。alloc位于core与std之间它提供的类型本身不依赖操作系统但所有动态分配最终都要落到一个内存分配器上。在std环境下Rust 标准库替你接好了默认分配器而在裸机环境下这个分配器必须由你自己提供——这正是alloc.md这一讲的核心内容。核心前提使用alloc必须实现全局堆分配器alloc文档alloc.md开门见山给出结论要使用alloc你必须实现一个全局堆分配器即实现 Rust 标准库中的GlobalAlloctraitcore::alloc::GlobalAlloc。GlobalAlloc是编译器与alloccrate 之间的桥梁。Vec::push、String::to_string、Box::new等操作在底层都会调用由#[global_allocator]标注的分配器实例上的alloc/dealloc方法。// 来自 alloc-example 的完整示例 // https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html完整示例为裸机程序接入buddy_system_allocator仓库中的 alloc-example/src/main.rs 是一个最小但完整的可编译示例。它使用buddy_system_allocator这个 crate 提供的LockedHeap来充当全局分配器#![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 只在这里使用且 entry 只会被调用一次。 unsafe { // 把堆内存交给分配器管理。 HEAP_ALLOCATOR.lock().init(raw mut HEAP as usize, HEAP_SIZE); } // 现在可以做任何需要堆分配的操作了。 let mut v Vec::new(); v.push(A string.to_string()); }该示例对应的 Cargo.toml 声明了两个关键依赖并在 dev / release 两个 profile 中都开启了panic abort[package] name alloc-example version 0.1.0 edition 2024 publish false [dependencies] buddy_system_allocator 0.13.0 panic-halt 1.0.0 [profile.dev] panic abort [profile.release] panic abort逐行拆解示例的五个关键点#![no_main]与#![no_std]这是一个裸机程序没有标准库提供的主函数与运行时。注意示例中pub fn entry()只是一个普通函数并未被链接器当作入口——这也是文档明确说明该示例能编译但无法运行因为它没有入口点的原因。真实场景下还需要链接脚本和汇编代码来引导执行参见 minimal.md 中关于没有main或任何其他入口点的说明。extern crate alloc;显式引入alloccrate使Vec、String、ToString等类型与方法可用。在no_std下alloc不会自动进入作用域。extern crate panic_halt as _;std自带 panic handler但no_std程序必须自己提供。panic_halt是一个提供挂起halt式 panic handler 的 crate用as _导入可以确保它被链接进最终二进制同时不需要在代码中显式使用它的任何符号。如果没有它链接阶段会因为缺少#[panic_handler]而失败。从 Cargo.lock 可以看到示例锁定的是panic-halt1.0.0 版本。#[global_allocator]静态变量static HEAP_ALLOCATOR: LockedHeap32被标注为全局分配器。一个二进制中只能存在一个全局分配器——这是编译器的硬性约束其原因是整个依赖树中的任何 crate 只要用到了alloc例如buddy_system_allocator内部的spin锁它们共享的都是这一个分配器实例。通常全局分配器应该定义在最顶层的二进制 crate 中。HEAP_ALLOCATOR.lock().init(...)分配器只是数据结构它并不知道从哪块内存去分配。在entry()中我们把静态数组HEAP65536字节即 64 KiB的地址和大小通过init交给LockedHeap。这一操作是不安全unsafe的因为static mut HEAP本身就要求 unsafe 访问初始化必须在entry首次执行堆分配之前恰好完成一次。源码注释中的// SAFETY块正是对这个不变量只在这里使用、只调用一次的说明。LockedHeap的 const 参数最大分配阶数LockedHeap32中的32是一个编译期常量参数表示 buddy 分配器的最大阶数max order。buddy system 算法按 2 的幂次把内存划分为不同大小的块阶数n对应块大小2^n字节最大阶数为32意味着该分配器理论上最多可以分配高达2^32字节4 GiB的连续区域实际能分配多大还受限于喂给它的堆内存总量本示例只有 64 KiB 的HEAP。阶数决定 buddy 分配器的最粗粒度它会直接影响内部维护的空闲块链表的规模和合并行为因此需要根据目标堆大小权衡选择。依赖关系一瞥从 Cargo.lock 可以看出依赖树非常简单alloc-example0.1.0 依赖buddy_system_allocator0.13.0 与panic-halt1.0.0buddy_system_allocator0.13.0 内部依赖spin0.10.0用于实现LockedHeap的锁。也就是说LockedHeap的lock()方法实际返回的是由spincrate 提供的内核态自旋锁保护的可变访问这保证了在多核 / 中断场景下堆分配操作是安全的。深入buddy_system_allocator两种用法comprehensive-rust 在 useful-crates/buddy_system_allocator.md 中明确指出buddy_system_allocator是一个实现了基础 buddy system 分配算法的 crate它有两种典型用法用法组件适用场景实现GlobalAlloc接入标准alloccrateLockedHeap本文alloc.md讲解的堆分配场景分配其他地址空间FrameAllocator例如为 PCI BAR 分配 MMIO 地址空间LockedHeap接入allocLockedHeap是GlobalAlloc的一个实现内部用自旋锁包裹Heap适合在裸机 / 内核环境中安全地提供堆分配。这正是上一节示例的做法把LockedHeap32声明为#[global_allocator]之后Vec、String、Box、format!等一切需要堆的类型都可以正常使用。FrameAllocator分配任意地址空间FrameAllocator不关心Rust 堆而是从你预先登记的地址区间中分配对齐的地址块。仓库中的 allocator-example/src/main.rs 给出了一个非常直观的示例——为 PCI 设备的 BAR基地址寄存器分配 MMIO 空间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); }这里的要点add_frame(0x200_0000, 0x400_0000)把0x200_0000~0x400_0000这段物理地址区间登记为可分配资源Layout::from_size_align(0x100, 0x100)声明需要一个 0x100256字节、对齐也是 0x100 的块——PCI BAR 的固有约束是地址对齐等于其大小因此用alloc_aligned分配非常契合运行方式在 src/bare-metal/useful-crates/allocator-example/ 目录下执行cargo run。该示例在 Rust Playground 中无法运行因为依赖了外部 crate对应 Cargo.toml 中声明的buddy_system_allocator 0.13.0。常见误区与注意事项综合alloc.md的details讲解与仓库源码以下边界必须牢记全局分配器必须恰好一个只要依赖树里任何一个 crate 依赖alloc你的二进制就必须定义且只能定义一个#[global_allocator]。定义位置通常选在最顶层的二进制 crate避免在库 crate 中定义导致重复。extern crate panic_halt as _;不能省它保证了 panic handler 被链接进来。若省略链接器会报缺少#[panic_handler]的错误。初始化时机分配器实例在内存中是一段静态数据结构必须在任何分配发生之前完成init且通常只初始化一次。示例用entry只调用一次作为 SAFETY 依据。panic abort的必要性在部分裸机目标上默认的 unwind 式 panic 会引用eh_personality符号导致链接失败因此示例在 dev 与 release profile 中都显式设置了panic abort。这一点在 minimal.md 中也有说明。示例本身不完整alloc-example能编译、不能运行——它没有入口点。把它落地到真实硬件还需要#[no_mangle] pub extern C fn _start或由链接脚本指定的其他入口、启动汇编与链接脚本这些内容属于本课程 Bare Metal 模块的后续章节如 aps.md 中的 aarch64 启动流程。在课程体系中的位置alloc.md位于课程 Bare Metal 模块bare-metal.md的no_std Rust部分紧跟在 no_std.md 之后是从no_std到可以运行复杂算法的关键一步。后续的 useful-crates 章节含 buddy_system_allocator.md、spin.md、allocator-example则进一步展示了同一 crate 家族在地址空间分配、锁等方向的应用。掌握了本文的堆分配器接入方法你就可以在裸机环境中放心使用Vec、String、format!等标准库集合把精力集中在真正的业务逻辑上。【免费下载链接】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),仅供参考