Основы ЭВМ
Теория: Адресация и страницы
В первых компьютерах программы обращались к памяти напрямую. Они записывали данные по конкретным адресам, и эти же ячейки могли использовать другие процессы. Пока система выполняла одну задачу, это не вызывало проблем. Но как только в памяти появлялось несколько программ, они начинали мешать друг другу: одна могла затереть данные другой. Это было и небезопасно, и неудобно.
Когда системы усложнились, появилась ещё одна трудность — фрагментация памяти. Свободные участки существовали, но были разбросаны по разным местам. Программе, которой требовался большой непрерывный блок, места не хватало, хотя суммарно памяти было достаточно.
Решением стала виртуальная память. Программа видит у себя ровное и непрерывное адресное пространство — будто вся память принадлежит только ей. На деле данные лежат в общей физической памяти, а каждый логический адрес переводится в физический.
Когда программа работает, она всегда использует логические адреса — воображаемые номера ячеек в своём пространстве. Физические адреса, то есть реальные позиции в памяти, вычисляются с помощью таблицы страниц. Чтобы такой перевод был возможен, память делится на одинаковые куски — страницы, обычно по 4 КБ.
Существуют и увеличенные страницы (huge pages) — размером от нескольких мегабайт. Они применяются там, где нужно уменьшить накладные расходы на хранение таблиц страниц, например в серверах или при работе с большими массивами данных.
Этот механизм называется paging, или страничная адресация.
Операционная система ведёт таблицу страниц (page table), где хранится соответствие между логическими и физическими страницами. А сам перевод адресов выполняет специальный блок внутри процессора — MMU (Memory Management Unit). MMU обращается к таблице, находит нужное сопоставление и подставляет физический адрес, не прерывая работу программы.
Такой механизм изолирует процессы друг от друга и позволяет системе свободно управлять размещением данных. Если оперативной памяти не хватает, неиспользуемые страницы можно временно выгрузить на диск, а при необходимости — вернуть обратно.
Есть ещё один важный момент — выравнивание. Процессор не работает с каждым байтом по отдельности, а считывает данные блоками фиксированного размера. На уровне инструкций это обычно 4 или 8 байт, а на уровне кэша — целая строка (cache line), чаще всего 64 байта.
Если переменная расположена так, что её начало совпадает с нужной границей, процессор получает данные одним обращением к памяти. Если же она «разъезжается» между двумя блоками, приходится читать две кэш-строки и склеивать нужные байты. Это приводит к лишним обращениям к памяти и замедляет работу.
Поэтому компилятор и процессор стараются размещать и выравнивать данные по определённым границам — чтобы доступ к ним укладывался в одну кэш-строку и выполнялся максимально быстро.
Разница между логическим и физическим адресом становится понятной на примере. Пусть размер страницы равен тысяче байт. Программа обращается к адресу 2500. Процессор делит его: это вторая страница и смещение пятьсот. В таблице страниц записано, что вторая логическая страница хранится в физическом блоке номер семь. Тогда реальный адрес будет семь тысяч плюс пятьсот, то есть 7500. Для программы это по-прежнему адрес 2500, но фактически данные находятся в другой области.
Такое преобразование решает сразу две задачи. Программы изолированы друг от друга, и каждая уверена, что память принадлежит только ей. При этом операционная система использует память гибко, раскладывая страницы туда, где есть место.

