tg_kernel_alloc/lib.rs
1//! 内存分配。
2//!
3//! 教程阅读建议:
4//!
5//! - 先看 `init` 与 `transfer`:理解“先初始化,再把可用内存交给分配器”;
6//! - 再看 `HEAP` / `GlobalAlloc`:理解 Rust `alloc` 如何落到内核堆实现。
7
8#![no_std]
9#![deny(missing_docs)]
10
11extern crate alloc;
12
13use alloc::alloc::handle_alloc_error;
14use core::{
15 alloc::{GlobalAlloc, Layout},
16 cell::UnsafeCell,
17 ptr::NonNull,
18};
19use customizable_buddy::{BuddyAllocator, LinkedListBuddy, UsizeBuddy};
20
21/// 静态单元格,用于安全地包装需要内部可变性的静态变量。
22///
23/// 这是对 `static mut` 的替代方案,通过 `UnsafeCell` 提供内部可变性,
24/// 同时避免了 `static mut` 带来的编译警告和潜在的未定义行为。
25///
26/// # Safety
27///
28/// 此类型实现了 `Sync`,但调用者必须确保:
29/// - 在单处理器环境下使用,或
30/// - 通过外部同步机制保证线程安全
31struct StaticCell<T> {
32 inner: UnsafeCell<T>,
33}
34
35// SAFETY: StaticCell 仅在单处理器环境下使用,不存在并发访问。
36unsafe impl<T> Sync for StaticCell<T> {}
37
38impl<T> StaticCell<T> {
39 /// 创建一个包含给定值的新 `StaticCell`。
40 const fn new(value: T) -> Self {
41 Self {
42 inner: UnsafeCell::new(value),
43 }
44 }
45
46 /// 获取内部值的可变指针。
47 ///
48 /// # Safety
49 ///
50 /// 调用者必须确保不存在对内部值的并发访问。
51 #[inline]
52 fn get(&self) -> *mut T {
53 self.inner.get()
54 }
55}
56
57/// 初始化内存分配。
58///
59/// 参数 `base_address` 表示动态内存区域的起始位置。
60///
61/// # 注意
62///
63/// 此函数必须在使用任何堆分配之前调用,且只能调用一次。
64#[inline]
65pub fn init(base_address: usize) {
66 // 初始化 buddy 分配器:设置最小块阶数 + 初始基址。
67 // SAFETY: 此函数只在内核初始化时调用一次,此时没有其他代码会访问 HEAP。
68 // base_address 由调用者保证是有效的堆起始地址。
69 heap_mut().init(
70 core::mem::size_of::<usize>().trailing_zeros() as _,
71 NonNull::new(base_address as *mut u8).unwrap(),
72 );
73}
74
75/// 将一个内存块托管到内存分配器。
76///
77/// # Safety
78///
79/// 调用者必须确保:
80/// - `region` 内存块与已经转移到分配器的内存块都不重叠
81/// - `region` 未被其他对象引用
82/// - `region` 必须位于初始化时传入的起始位置之后
83/// - 内存块的所有权将转移到分配器
84#[inline]
85pub unsafe fn transfer(region: &'static mut [u8]) {
86 // 将一段“现成内存”并入堆。常用于把启动后可回收区域纳入分配器管理。
87 let ptr = NonNull::new(region.as_mut_ptr()).unwrap();
88 // SAFETY: 由调用者保证内存块有效且不重叠
89 heap_mut().transfer(ptr, region.len());
90}
91
92/// 堆分配器。
93///
94/// 最大容量:6 + 21 + 3 = 30 -> 1 GiB。
95/// 不考虑并发使用,因此没有加锁。
96///
97/// 使用 `StaticCell` 包装以避免 `static mut` 的使用,
98/// 通过 `heap_mut()` 函数获取可变引用。
99static HEAP: StaticCell<BuddyAllocator<21, UsizeBuddy, LinkedListBuddy>> =
100 StaticCell::new(BuddyAllocator::new());
101
102/// 获取堆分配器的可变引用。
103///
104/// # Safety
105///
106/// 此函数内部使用 unsafe 获取可变引用,仅在单处理器环境下安全。
107/// 调用者必须确保不存在对 HEAP 的并发访问。
108#[inline]
109fn heap_mut() -> &'static mut BuddyAllocator<21, UsizeBuddy, LinkedListBuddy> {
110 // SAFETY: 仅在单处理器环境下使用,不存在并发访问。
111 unsafe { &mut *HEAP.get() }
112}
113
114struct Global;
115
116#[global_allocator]
117static GLOBAL: Global = Global;
118
119// SAFETY: GlobalAlloc 的实现必须是 unsafe 的。
120// 此实现仅用于单处理器环境,不支持并发访问。
121unsafe impl GlobalAlloc for Global {
122 #[inline]
123 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
124 // SAFETY: 通过 heap_mut() 访问分配器,在单处理器环境下不会有并发的分配请求。
125 // layout 的有效性由调用者(Rust 的 alloc 机制)保证。
126 if let Ok((ptr, _)) = heap_mut().allocate_layout::<u8>(layout) {
127 ptr.as_ptr()
128 } else {
129 handle_alloc_error(layout)
130 }
131 }
132
133 #[inline]
134 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
135 // SAFETY: 通过 heap_mut() 访问分配器,在单处理器环境下不会有并发的释放请求。
136 // ptr 和 layout 的有效性由调用者保证(必须是之前 alloc 返回的)。
137 heap_mut().deallocate_layout(NonNull::new(ptr).unwrap(), layout)
138 }
139}