Files
Elyz/kernel/docs/allocator.md
2026-07-06 00:25:49 +03:00

3.1 KiB
Raw Blame History

Аллокатор кучи

Файл: 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 — доступ к внутренним данным
  • Dropstore(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

  1. Проверяет large_block_free — если есть подходящий блок, отдаёт его
  2. Иначе bump-аллокация: выравнивание, проверка next_bump + size <= heap_end, возврат next_bump
  3. При OOM возвращает null

Глобальный аллокатор

#[global_allocator]
pub static ALLOCATOR: Locked<SlabAllocator>;

GlobalAlloc::alloc(&self, layout) -> *mut u8

  1. Lock allocator
  2. Если layout.size() влезает в slab класс:
    • Находит list_index
    • Если free list не пуст: pop голову, вернуть указатель
    • Если пуст: fallback_alloc(layout) но с размером slab-блока (не layout.size())
  3. Для блоков > 2048: сразу fallback_alloc(layout)

GlobalAlloc::dealloc(&self, ptr, layout)

  1. Lock allocator
  2. Для slab-sized: push обратно в slab free list как ListNode
  3. Для больших: push в large_block_free как LargeBlockNode