Блог "Дети ОИТа"
/поиск

Виртуальная память (virtual memory)

Виртуальная память (virtual memory) – это фундаментальная концепция современных операционных систем, которая создаёт для каждого процесса иллюзию непрерывного, изолированного адресного пространства, ч

Виртуальная память (virtual memory) – это фундаментальная концепция современных операционных систем, которая создаёт для каждого процесса иллюзию непрерывного, изолированного адресного пространства, часто большего, чем доступная физическая память. Она абстрагирует физические адреса, позволяя использовать файлы подкачки, разделяемую память, кэширование и защиту. В этой статье подробно рассматриваются аппаратные и программные механизмы, лежащие в основе виртуальной памяти: устройство управления памятью (MMU), страничная организация, трансляция адресов, таблицы страниц, TLB, подкачка (swapping), кэширование страниц, а также практические аспекты управления виртуальной памятью в Linux. Приводятся примеры кода и диаграммы для визуализации.

1.1. Английские сокращения

Сокращение Английское обозначение Русское обозначение
MMU Memory Management Unit Устройство управления памятью
TLB Translation Lookaside Buffer Буфер трансляции адресов
VA Virtual Address Виртуальный адрес
PA Physical Address Физический адрес
PT Page Table Таблица страниц
PTE Page Table Entry Запись таблицы страниц
PDE Page Directory Entry Запись каталога страниц
PDPTE Page Directory Pointer Table Entry Запись таблицы указателей каталога
PML4E Page Map Level 4 Entry Запись карты страниц 4-го уровня
CR3 Control Register 3 Управляющий регистр CR3 (база таблиц страниц)
NX/XD No‑Execute / Execute Disable Запрет исполнения
ASLR Address Space Layout Randomization Рандомизация размещения адресного пространства
KASLR Kernel Address Space Layout Randomization Рандомизация адресного пространства ядра
THP Transparent Huge Pages Прозрачные большие страницы
HUGETLB Huge TLB (большие страницы) Большие страницы (2 МБ / 1 ГБ)
COW Copy‑on‑Write Копирование при записи
LRU Least Recently Used Наименее недавно использованный
OOM Out‑of‑Memory Исчерпание памяти
VMA Virtual Memory Area Область виртуальной памяти
OS Operating System Операционная система
IPC Inter‑Process Communication Межпроцессное взаимодействие

1.2. Русские сокращения

Сокращение Обозначение
УУП Устройство управления памятью
БТА Буфер трансляции адресов
ВА Виртуальный адрес
ФА Физический адрес
ТС Таблица страниц
ЗТС Запись таблицы страниц
ПС Подкачка страниц
КС Кэш страниц
ПБС Прозрачные большие страницы
КПЗ Копирование при записи
ИП Исчерпание памяти
ОПВ Область виртуальной памяти

2.1. Зачем нужна виртуальная память

  • Изоляция процессов: каждый процесс работает в своём адресном пространстве, не мешая другим.
  • Абстракция памяти: процесс не знает о физическом расположении данных, что упрощает разработку.
  • Эффективное использование физической памяти: неактивные страницы могут быть выгружены на диск (swapping).
  • Разделение кода и данных: несколько процессов могут совместно использовать одни и те же страницы (например, библиотеки).
  • Защита: страницы могут быть помечены как доступные только для чтения, запрещённые для исполнения и т.д.

2.2. Базовые компоненты

  • Виртуальное адресное пространство – логическое представление адресов, которое видит процесс.
  • Физическая память – реальные ячейки ОЗУ.
  • MMU – аппаратное устройство, транслирующее виртуальные адреса в физические.
  • Таблицы страниц – структуры данных, хранящие отображения VA->PA.
  • Подкачка – перемещение страниц между ОЗУ и диском.

2.3. Модель страничной организации

Виртуальное адресное пространство и физическая память разбиты на блоки фиксированного размера – страницы (обычно 4 КБ). Процесс ссылается на виртуальные страницы, а MMU преобразует их в физические страницы через таблицы страниц. Если страница отсутствует в физической памяти, возникает страничное прерывание (page fault), и ОС подгружает её с диска.

3.1. Устройство управления памятью (MMU)

MMU – часть процессора, выполняющая трансляцию адресов. При каждом обращении к памяти MMU:

  1. Извлекает из виртуального адреса номер виртуальной страницы и смещение.
  2. Проверяет, есть ли соответствующая запись в TLB (быстром кэше).
  3. Если в TLB есть, получает физический адрес страницы.
  4. Если в TLB нет, выполняет обход таблиц страниц (page walk) – читает таблицы из памяти, проверяет права доступа, загружает запись в TLB.
  5. Если страница отсутствует (бит P=0), генерирует page fault, передавая управление ОС.

3.2. TLB (Translation Lookaside Buffer)

TLB – это аппаратный кэш недавних трансляций (обычно 16–4096 записей). Он критичен для производительности, так как обращение к таблицам страниц в оперативной памяти требует нескольких циклов. При переключении контекста (смене CR3) TLB обычно сбрасывается (или помечается тегами ASID для сохранения).

3.3. Диаграмма работы TLB

@startuml
title Обращение к памяти с TLB
rectangle "Виртуальный адрес" as va
rectangle "TLB" as tlb
rectangle "Таблицы страниц (память)" as pt
rectangle "Физическая память" as phys

va -down-> tlb : "поиск по тегу (VPN)"
tlb -down-> pt : "если TLB miss\n(аппаратный page walk)"
pt -down-> phys : "получение PTE и проверка прав"
note right: При TLB hit\nсразу формируется PA
@enduml

4.1. Страницы и флаги

Каждая страница описывается записью PTE, содержащей:

  • Базовый физический адрес страницы (с выравниванием).
  • Биты присутствия (P), чтения/записи (R/W), пользователь/супервизор (U/S), NX, кэширования и др.

4.2. Многоуровневые таблицы страниц (x86-64)

Чтобы экономить память, таблицы страниц организованы иерархически. Для 48‑битных адресов используются 4 уровня: PML4, PDP, PD, PT. Каждая запись указывает на таблицу следующего уровня (или физическую страницу). Благодаря этому можно не выделять таблицы для неиспользуемых областей.

4.3. Диаграмма трансляции (упрощённая)

@startuml
title Трансляция виртуального адреса в физический (x86-64)
rectangle "CR3" as cr3
rectangle "PML4" as pml4
rectangle "PDP" as pdp
rectangle "PD" as pd
rectangle "PT" as pt
rectangle "Физическая страница" as phys
rectangle "Виртуальный адрес\n(47-39) PML4 индекс\n(38-30) PDP индекс\n(29-21) PD индекс\n(20-12) PT индекс\n(11-0) смещение" as va

cr3 -down-> pml4 : "база PML4"
va -right-> pml4 : "индекс"
pml4 -down-> pdp : "запись указывает на PDP"
va -right-> pdp : "индекс"
pdp -down-> pd : "запись указывает на PD"
va -right-> pd : "индекс"
pd -down-> pt : "запись указывает на PT"
va -right-> pt : "индекс"
pt -down-> phys : "PTE указывает на страницу"
va -right-> phys : "смещение"
note right: Проверка прав на каждом уровне
@enduml

5.1. Принцип подкачки

Когда физическая память заполняется, ОС выбирает страницу для выгрузки на диск (обычно по алгоритму LRU или его вариантам). Освободившееся место используется для загрузки требуемой страницы. Выгруженные страницы хранятся в разделе подкачки (swap partition) или файле подкачки (pagefile.sys). При обращении к отсутствующей странице возникает page fault, и ОС загружает её обратно.

5.2. Алгоритмы замещения страниц

  • LRU (Least Recently Used) – вытесняется страница, к которой дольше всего не обращались.
  • FIFO – простая очередь, но часто неэффективна.
  • Second Chance – модификация FIFO с битом использования.
  • Clock – алгоритм «часов» (аппаратно оптимизированная версия LRU).

В Linux используется комбинация LRU с активными/неактивными списками.

5.3. Диаграмма подкачки страниц

@startuml
title Процесс подкачки страницы
rectangle "Процесс" as proc
rectangle "Физическая память" as ram
rectangle "Раздел подкачки" as swap

proc -down-> ram : "обращение к странице, отсутствующей в RAM"
ram -down-> swap : "страница выгружена на диск"
swap -up-> ram : "страница загружается обратно в RAM"
note right: При page fault ОС загружает страницу,\nпри необходимости выгружая другую.
@enduml

5.4. Стратегии управления памятью

  • Demand paging – страницы загружаются только при обращении.
  • Prefetching – упреждающая загрузка соседних страниц.
  • Copy‑on‑Write – при fork() страницы не копируются, пока не будет записи.

6.1. Page Cache

ОС кэширует содержимое файлов в оперативной памяти (page cache) для ускорения последующих чтений. При записи данные сначала попадают в кэш, а затем асинхронно записываются на диск (dirty pages). В Linux это реализовано через структуру address<sub>space</sub> и radix tree. Кэш также используется для отображения файлов в память (mmap).

6.2. Буферный кэш

В старых системах был отдельный буферный кэш для блоков устройств; в современных ОС он интегрирован с page cache (единый кэш страниц).

7.1. Структура адресного пространства процесса

В Linux каждому процессу соответствует структура `mm<sub>struct</sub>`, содержащая список областей виртуальной памяти (VMA). Каждая VMA описывает непрерывный регион (например, код, данные, куча, стек, mmap). При обращении к адресу, не попадающему ни в одну VMA, генерируется SIGSEGV.

7.2. Системные вызовы

  • `mmap()` – создание отображения файла или анонимной памяти.
  • `munmap()` – удаление отображения.
  • `mprotect()` – изменение прав доступа.
  • `brk()` / `sbrk()` – изменение размера кучи (устаревает, вместо него используют mmap).
  • `madvise()` – советы ядру по использованию памяти.
  • `mlock()` – фиксация страниц в ОЗУ (запрет выгрузки).

7.3. Просмотр памяти процесса

Файлы `/proc/pid/maps` показывают все VMA, `/proc/pid/smaps` – расширенную информацию (размер, количество страниц, флаги).

7.4. Пример на C: использование mmap и mprotect

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>

int main() {
    size_t size = 4096;
    void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (addr == MAP_FAILED) {
        perror("mmap");
        return 1;
    }
    strcpy((char*)addr, "Hello, virtual memory!");
    printf("Данные: %s\n", (char*)addr);
    // Меняем права на только чтение
    if (mprotect(addr, size, PROT_READ) == -1) {
        perror("mprotect");
    }
    // Попытка записи вызовет SIGSEGV
    // ((char*)addr)[0] = 'X';
    munmap(addr, size);
    return 0;
}

7.5. Просмотр /proc/self/maps

cat /proc/self/maps

8.1. Большие страницы (Huge Pages)

Стандартный размер страницы 4 КБ приводит к большому числу записей в TLB и частым промахам. Использование страниц 2 МБ или 1 ГБ (HugeTLB) уменьшает число TLB‑записей, улучшая производительность для приложений с большим объёмом памяти. В Linux поддерживаются через hugetlbfs и через прозрачные огромные страницы (THP).

8.2. Прозрачные огромные страницы (THP)

THP автоматически объединяет смежные 4‑КБ страницы в 2‑МБ, если это возможно. Управляется через `/sys/kernel/mm/transparent<sub>hugepage</sub>/`. Может быть включен (always), по запросу (madvise) или отключен (never).

8.3. ASLR и KASLR

ASLR – рандомизация адресов в пользовательском пространстве (стек, куча, библиотеки). KASLR – аналогично для ядра. Это затрудняет эксплуатацию уязвимостей.

8.4. Memory Protection Keys (MPK)

Аппаратное расширение Intel для управления доступом к страницам через ключи, без изменения таблиц страниц. Позволяет быстро переключать права для групп страниц.

ОС Страница по умолчанию Подкачка Механизм Большие страницы
Linux 4 КБ swap partition/file mmap, mprotect THP, HugeTLB
Windows 4 КБ pagefile.sys VirtualAlloc, VirtualProtect Large Pages
macOS (XNU) 4 КБ dynamic swap mmap, mach<sub>vm</sub>_allocate Super Pages (2 МБ)
FreeBSD 4 КБ swap mmap superpages
  1. Объясните, почему TLB критичен для производительности.
  2. Что произойдёт при обращении к странице с битом P=0?
  3. В чём разница между mmap и brk?
  4. Какие преимущества дают большие страницы?
  5. Как посмотреть количество свободной памяти и использование swap в Linux?
  6. Напишите программу, которая выделяет 10 МБ анонимной памяти, записывает данные и показывает её VMA через /proc/self/maps.

Виртуальная память – сложный, но хорошо отлаженный механизм, сочетающий аппаратную поддержку (MMU, TLB) и программные алгоритмы (подкачка, кэширование). Она обеспечивает изоляцию, безопасность и эффективное использование ресурсов. Понимание её работы необходимо для оптимизации производительности и разработки надёжных приложений. Современные расширения (THP, MPK, ASLR) продолжают эволюционировать, отвечая на вызовы производительности и безопасности.

Команда Описание
`free -h` Показать использование памяти и swap
`vmstat 2` Статистика памяти и подкачки
`cat /proc/meminfo` Детальная информация о памяти
`cat /proc/pid/smaps` Расширенная информация о VMA процесса
`sudo swapoff -a && swapon -a` Перезапустить swap
`echo madvise > /sys/kernel/mm/transparent<sub>hugepage</sub>/enabled` Включить THP по запросу

Примеры на C компилируются с GCC. Для работы с mmap не требуются права root. Для просмотра /proc/self/maps можно использовать любой терминал. Код предназначен для иллюстрации и может быть адаптирован.