# Управление памятью: концептуальная модель ## Архитектурная философия Управление памятью в Elyz построено как **трёхуровневая иерархия**, где каждый уровень решает свою задачу и взаимодействует с соседними через строго определённые интерфейсы. ``` Уровень 1: BitmapPMM (глобальный, физический) │ │ предоставляет сырые фреймы ▼ Уровень 2: PMActor + BuddyAllocator (распределённый, физический) │ │ управляет диапазонами, выдаёт под-диапазоны ▼ Уровень 3: AddressSpace (VMM) (виртуальный) │ │ отображает физические фреймы в виртуальные адреса ▼ CPU (MMU, page tables) ``` ### Зачем три уровня? - **BitmapPMM** — глобальный аллокатор физических фреймов. Простой, надёжный, но неэффективный для частых alloc/free маленьких блоков. Используется для начальной загрузки и для Page Table страниц. - **BuddyAllocator + PMActor** — распределённая модель. Каждый актор управляет своим диапазоном физической памяти через buddy-алгоритм. Это даёт: (1) изоляцию — актор A не может истощить память актора B; (2) масштабирование — акторы могут работать параллельно; (3) предсказуемость — каждый актор знает свой лимит. - **AddressSpace (VMM)** — виртуальные адресные пространства. PML4, VMA-деревья, copy-on-write, lazy mapping. Использует PMM для аллокации Page Table страниц (через `pmm_alloc` в `paging.rs`). ### Разделение ответственности по файлам | Файл | Компонент | Роль в абстракции | |------|-----------|-------------------| | `address.rs` | PhysAddr / VirtAddr / HHDM | Базовые типы для адресов | | `pmm.rs` | BitmapPMM | Глобальный менеджер физических фреймов | | `buddy.rs` | BuddyAllocator | O(1) buddy allocator (intrusive list) | | `pm_manages.rs` | PMActor + PMActorQueue | Актёр физической памяти + MPSC очередь | | `pm_router.rs` | PMRouter | Lock-free маршрутизация запросов/ответов | | `paging.rs` | PageTable | Аппаратные 4-уровневые page tables | | `vmm.rs` | AddressSpace + VMA | Виртуальные адресные пространства | | `allocator.rs` | SlabAllocator | Кучевой аллокатор (global_allocator) | | `mod.rs` | Экспорт | Фасад подсистемы | ### Поток данных: типичная аллокация ``` Процесс A хочет 16 страниц: 1. Код пользователя отправляет PMRequest::Allocate в PMActor через PMRouter::alloc_channel() + actor.submit_request() 2. Когда актор получает CPU, process_messages() вызывает buddy.alloc_pages(16) 3. BuddyAllocator находит блок порядка 4 (2^4 = 16) или больше, разбивает его при необходимости 4. PMActor создаёт Capability с CapObject::Memory { phys, size_pages } 5. Ответ (PMResponse::Allocated) отправляется через PMRouter 6. Получатель может отобразить фреймы в своё AddressSpace через map_region() или map_shared() ``` ### Поток данных: page fault ``` CPU ловит #PF (page fault): 1. interrupt.rs: rust_page_fault_handler() читает CR2 2. Получает блокировку KERNEL_SPACE 3. process_pending_revocations() — обрабатывает накопленные отзывы 4. handle_fault() проверяет: это COW? это lazy region? 5. Если COW — копируем страницу (copy-on-write) 6. Если lazy — alloc_frame() из PMM + map_page() 7. Если нераспознанный fault — KERNEL PANIC ```