Страничная организация памяти
Как ОС превращает «адрес 9000» в реальное место в микросхеме памяти.
Страничная организация (paging) делит виртуальную память на страницы, а физическую — на кадры одинакового размера; таблица страниц задаёт, какой странице какой кадр соответствует.
Зачем делить память на страницы
Если выдавать процессам память сплошными непрерывными кусками, быстро возникает фрагментация: свободная память есть, но разбита на мелкие дыры, в которые большой запрос не влезает. Paging решает это: память нарезается на одинаковые блоки фиксированного размера, и процессу выдаётся любой свободный набор блоков — необязательно соседних.
- Страница (page) — блок виртуальной памяти.
- Кадр (frame) — блок физической памяти того же размера.
Типичный размер страницы — 4 КБ (4096 байт). Страница процесса может лежать в любом свободном кадре, и они даже не обязаны идти подряд.
Таблица страниц
Таблица страниц — это «карта»: для каждой виртуальной страницы она хранит номер физического кадра, где страница лежит. У каждого процесса своя таблица — отсюда и изоляция.
| Виртуальная страница | Физический кадр |
| 0 | 5 |
| 1 | 9 |
| 2 | 2 |
| 3 | 7 |
Как транслируется адрес
Виртуальный адрес разбивается на две части:
- Номер страницы = адрес ÷ размер страницы (целочисленно).
- Смещение = адрес mod размер страницы (где внутри страницы).
Дальше по таблице страниц находим кадр и собираем физический адрес: физический = кадр × размер_страницы + смещение.
def translate(virtual, page_size, page_table):
page = virtual // page_size # номер страницы
offset = virtual % page_size # смещение внутри страницы
if page not in page_table:
print(f"Адрес {virtual}: страница {page} не в таблице -> page fault")
return
frame = page_table[page]
physical = frame * page_size + offset
print(f"Виртуальный адрес {virtual}:")
print(f" страница = {virtual} // {page_size} = {page}")
print(f" смещение = {virtual} % {page_size} = {offset}")
print(f" страница {page} -> кадр {frame}")
print(f" физический = {frame} * {page_size} + {offset} = {physical}")
page_table = {0: 5, 1: 9, 2: 2, 3: 7}
translate(9000, 4096, page_table)
Вывод:
Виртуальный адрес 9000: страница = 9000 // 4096 = 2 смещение = 9000 % 4096 = 808 страница 2 -> кадр 2 физический = 2 * 4096 + 808 = 9000
Цена трансляции и TLB
Каждое обращение к памяти теперь требует ещё одного обращения — к таблице страниц. Это удвоило бы стоимость! Поэтому процессор кэширует недавние трансляции в специальном кэше — TLB (Translation Lookaside Buffer). Если нужная запись в TLB (TLB hit), трансляция почти бесплатна. Здесь опять выручает локальность: программы обращаются к небольшому набору страниц, и они быстро оседают в TLB.
Многоуровневые таблицы
Для 64-битных адресов плоская таблица страниц была бы гигантской. Поэтому таблицы делают многоуровневыми (таблица таблиц): хранятся только реально используемые части. Это экономит память на саму таблицу страниц.
Итог
- Paging делит виртуальную память на страницы, а физическую — на кадры равного размера.
- Таблица страниц сопоставляет страницам кадры; у каждого процесса она своя.
- Адрес = номер страницы + смещение; физический = кадр × размер + смещение.
- TLB кэширует трансляции, чтобы paging не удваивал стоимость доступа.
- Многоуровневые таблицы экономят память на огромных адресных пространствах.