3.1 KiB
3.1 KiB
Аллокатор кучи
Файл: src/mem/allocator.rs
Slab-аллокатор со spinlock-синхронизацией и fallback bump-аллокацией для больших блоков.
Примитив синхронизации: Locked<A>
Самодельный spinlock (альтернатива spin::Mutex).
pub struct Locked<A> {
inner: UnsafeCell<A>,
lock: AtomicBool,
}
new(inner)— создаёт с unlocked состояниемlock() -> LockedGuard<'_, A>— CAS-цикл наAtomicBool(спин-ожидание)
LockedGuard
Deref/DerefMut— доступ к внутренним даннымDrop—store(false, Release)— освобождение блокировки
unsafe impl<A> Sync for Locked<A> — разработчик гарантирует корректность.
SlabAllocator
pub struct SlabAllocator {
list_heads: [Option<&'static mut ListNode>; 9], // slab free list
large_block_free: Option<&'static mut LargeBlockNode>, // free list для >2048
heap_start: usize,
heap_end: usize,
next_bump: usize,
}
Slab классы
const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048];
9 классов — степени двойки от 8 до 2048.
ListNode / LargeBlockNode
struct ListNode {
next: Option<&'static mut ListNode>,
}
struct LargeBlockNode {
size: usize,
next: Option<&'static mut LargeBlockNode>,
}
Односвязные списки свободных блоков.
init(&mut self, start, size)
Устанавливает границы кучи: heap_start = start, heap_end = start + size, next_bump = start.
list_index(layout) -> Option<usize>
Находит индекс наименьшего slab-класса, покрывающего max(size, align).
Пример: layout size=20, align=8 → блок 20 > 16, следующий 32 → index 3 (32 байта).
fallback_alloc(&mut self, layout) -> *mut u8
- Проверяет
large_block_free— если есть подходящий блок, отдаёт его - Иначе bump-аллокация: выравнивание, проверка
next_bump + size <= heap_end, возвратnext_bump - При OOM возвращает null
Глобальный аллокатор
#[global_allocator]
pub static ALLOCATOR: Locked<SlabAllocator>;
GlobalAlloc::alloc(&self, layout) -> *mut u8
- Lock allocator
- Если
layout.size()влезает в slab класс:- Находит
list_index - Если free list не пуст: pop голову, вернуть указатель
- Если пуст:
fallback_alloc(layout)но с размером slab-блока (неlayout.size())
- Находит
- Для блоков > 2048: сразу
fallback_alloc(layout)
GlobalAlloc::dealloc(&self, ptr, layout)
- Lock allocator
- Для slab-sized: push обратно в slab free list как ListNode
- Для больших: push в large_block_free как LargeBlockNode