106 lines
3.1 KiB
Markdown
106 lines
3.1 KiB
Markdown
# Аллокатор кучи
|
||
|
||
**Файл**: `src/mem/allocator.rs`
|
||
|
||
Slab-аллокатор со spinlock-синхронизацией и fallback bump-аллокацией для больших блоков.
|
||
|
||
---
|
||
|
||
## Примитив синхронизации: `Locked<A>`
|
||
|
||
Самодельный spinlock (альтернатива `spin::Mutex`).
|
||
|
||
```rust
|
||
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
|
||
|
||
```rust
|
||
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 классы
|
||
|
||
```rust
|
||
const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048];
|
||
```
|
||
|
||
9 классов — степени двойки от 8 до 2048.
|
||
|
||
### ListNode / LargeBlockNode
|
||
|
||
```rust
|
||
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
|
||
|
||
---
|
||
|
||
## Глобальный аллокатор
|
||
|
||
```rust
|
||
#[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
|