Симптом: вы видите виртуальный адрес в отладчике и не понимаете, где физически лежат данные.
Самое быстрое решение: разделите путь чтения на четыре уровня — виртуальный адрес, физический адрес, кэш и внутреннее распределение DRAM.
CPU не берёт указатель из программы и не обращается им напрямую к конкретной микросхеме RAM. Сначала адрес проходит трансляцию через TLB или таблицы страниц, затем проверяется кэш, а при промахе запрос принимает контроллер памяти. Только после этого физический адрес раскладывается в координаты подсистемы DRAM — канал, ранг, банк, строку и столбец. Точные правила последнего шага зависят от конкретной платформы.
Эта статья предназначена для трёх групп читателей: для тех, кто путает виртуальный и физический адреса; для инженеров, анализирующих кэш-промахи и задержки памяти; а также для разработчиков, изучающих таблицы страниц, Rowhammer и виртуализацию.
Одна Load-инструкция как карта всей системы
Представьте инструкцию, которая загружает значение по адресу указателя:
mov rax, [rdi]
Регистр rdi содержит адрес, который текущий процесс считает допустимым. В типичной пользовательской программе это виртуальный адрес, а не координата ячейки RAM. Процессор передаёт его механизму управления памятью — MMU.
Дальше происходит несколько разных операций:
- процессор определяет виртуальную страницу и смещение внутри неё;
- MMU ищет перевод виртуальной страницы в TLB;
- при промахе выполняется обход таблиц страниц;
- получается физический адрес;
- кэш проверяет, есть ли нужная строка данных;
- при промахе кэша запрос отправляется контроллеру памяти;
- контроллер выбирает канал, ранг, банк, строку и столбец DRAM;
- данные возвращаются через кэш обратно к исполнительному блоку CPU.
Главная ошибка в объяснениях этой темы — смешивать стоимость разных этапов. Промах TLB, промах кэша и медленное обращение к DRAM — не одно и то же событие. Они могут произойти последовательно, но диагностируются разными счётчиками и требуют разных мер оптимизации.
Почему программа работает с виртуальным адресом
Виртуальная память даёт каждому процессу собственное адресное пространство. Два разных процесса могут использовать одинаковое числовое значение указателя, но оно будет указывать на разные физические страницы. Операционная система хранит отдельные наборы таблиц страниц и переключает корневую структуру при смене адресного пространства.
Это решает сразу несколько задач:
- изолирует память процессов друг от друга;
- позволяет размещать код и данные без знания конкретного расположения RAM;
- поддерживает отображение файлов и разделяемой памяти;
- даёт возможность временно выгружать страницы или создавать отображения методом Copy-on-Write;
- упрощает работу загрузчика, библиотек и механизмов защиты.
В Linux таблицы страниц описываются как иерархия, которая связывает виртуальные адреса с физическими адресами. Архитектурный код конкретного процессора определяет, сколько уровней реально используется и как они представлены. Подробное описание структуры можно сверить с документацией Linux по таблицам страниц.
Поэтому выражение «адрес переменной» без уточнения недостаточно. В исходном коде, отладчике и машинной инструкции вы чаще всего имеете дело с виртуальным адресом. Физический адрес появляется после работы MMU. Адрес DRAM-компонента формируется ещё позже — внутри контроллера памяти.
Что именно хранит виртуальный адрес
Для базовой страницы адрес удобно разделить на две части:
виртуальный адрес = номер виртуальной страницы + смещение внутри страницы
Номер страницы участвует в трансляции. Смещение не меняется: если нужный байт находился, например, в середине страницы, после перевода он остаётся на том же смещении внутри физического кадра.
В простом случае:
виртуальная страница → физический кадр
смещение → то же смещение
Размер страницы влияет на обе стороны производительности. Маленькие страницы позволяют точнее распределять память, но требуют большего количества записей в таблицах и TLB. Большие страницы покрывают больше адресного пространства одной записью, однако могут привести к лишнему расходу памяти и сложностям выделения непрерывных областей.
В документации ядра Linux для распространённых x86-конфигураций рассматриваются страницы размером 4 KiB, а также крупные страницы порядка 2 MiB и 1 GiB, если это поддерживается архитектурой и настройками системы. Эти значения нельзя автоматически переносить на любую архитектуру. Дополнительные детали приведены в описании многоуровневых таблиц страниц Linux.
Как TLB ускоряет перевод адреса
TLB — это небольшой аппаратный кэш соответствий между виртуальными страницами и физическими кадрами. Его смысл прост: если программа обращается к нескольким байтам в одной странице, процессору не нужно каждый раз заново читать таблицы страниц.
При попадании в TLB процессор быстро получает физический кадр, объединяет его со смещением и продолжает проверку доступа. В этот момент таблица страниц может вообще не читаться из основной памяти.
Если TLB не содержит нужного перевода, возникает TLB miss. Дальнейшее поведение зависит от архитектуры:
- аппаратный page walk самостоятельно читает записи таблиц;
- часть промежуточных записей может находиться в специальных кэшах обхода;
- при ошибке доступа процессор передаёт управление обработчику исключения;
- после успешного перевода новая запись обычно помещается в TLB.
В архитектурных руководствах современных процессоров TLB описывается именно как средство кэширования переводов, а таблицы страниц остаются структурами в памяти. Для проверки деталей конкретного семейства процессоров используйте Intel 64 and IA-32 Architectures Software Developer’s Manual и руководство разработчика AMD64.
Что происходит при обходе таблиц страниц
Таблица страниц сама хранится в памяти. Это не означает, что процессор теряет информацию о том, где находится её начало. Архитектура предоставляет специальный регистр или набор управляющих структур, через которые MMU получает корень текущего адресного пространства.
Упрощённый обход выглядит так:
- MMU берёт корень таблицы страниц текущего процесса.
- Часть битов виртуального адреса выбирает запись верхнего уровня.
- Полученное значение указывает на следующую таблицу.
- Следующая группа битов выбирает запись следующего уровня.
- Процесс продолжается до записи, содержащей номер физического кадра.
- Проверяются флаги присутствия, чтения, записи, исполнения и пользовательского доступа.
- Номер физического кадра объединяется со смещением.
Современные системы используют многоуровневые таблицы, чтобы не резервировать полный массив записей для огромного виртуального пространства. Неиспользуемые области могут не иметь промежуточных таблиц.
Если запись отсутствует или доступ запрещён, это не всегда означает одинаковую ошибку. Возможны разные варианты:
- страница ещё не была выделена из-за ленивого выделения;
- сработал Copy-on-Write;
- страница была выгружена в swap;
- адрес нарушает права доступа;
- процесс обратился к несуществующей области.
Linux обрабатывает такие ситуации через механизм page fault. Документация ядра отдельно описывает ошибки нехватки памяти, SIGSEGV, повторные попытки и чтение страницы с накопителя. Общие интерфейсы управления памятью собраны в документации Linux Memory Management API.
Практический вывод для отладки: счётчик page faults нельзя безоговорочно трактовать как «RAM стала медленной». Большая часть page faults может быть ожидаемой и быстро обработанной без чтения с диска. Гораздо важнее отличать minor fault от major fault и сопоставлять их с реальной нагрузкой на хранилище.
Физический адрес не равен адресу микросхемы
После успешной трансляции получается физический адрес. Он относится к физическому адресному пространству системы, но ещё не говорит вам, какая микросхема, сторона модуля или строка DRAM будет активирована.
Физический адрес можно представить как числовой запрос к контроллеру памяти:
физический адрес → функция адресного отображения → координаты DRAM
Контроллер может использовать разные группы битов для выбора:
- канала памяти;
- модуля или ранга;
- группы банков;
- банка;
- строки;
- столбца;
- отдельных внутренних режимов адресации.
Причём распределение может быть не простым последовательным разрезанием двоичного числа. Некоторые биты могут комбинироваться логической операцией XOR, чтобы равномернее распределять обращения между банками или каналами.
Исследования DRAM-адресации показывают, что внутренние функции отображения часто приходится восстанавливать экспериментально по задержкам и конфликтам строк. Работа DRAMA в материалах USENIX Security использует именно такую зависимость между физическими адресами и внутренними банковыми конфликтами.
Поэтому универсальная схема вида «биты 6–8 — столбец, биты 9–11 — банк» может быть учебной иллюстрацией, но не доказательством для вашей машины.
Как кэш меняет путь чтения
После формирования физического адреса CPU не обязан обращаться к DRAM. Сначала проверяется иерархия кэшей. Конкретные уровни, размеры, ассоциативность и политика замещения зависят от модели процессора.
Если нужная строка уже находится в подходящем кэше, данные возвращаются без обращения к контроллеру DRAM. Если её нет, происходит cache miss, и система запрашивает строку данных из следующего уровня или из основной памяти.
Для производительности полезно разделять три задержки:
- Задержка трансляции — сколько стоит поиск перевода в TLB или обход таблиц.
- Задержка кэша — сколько времени занимает обнаружение отсутствия строки и поиск на следующем уровне.
- Задержка DRAM — сколько требуется контроллеру и памяти для выполнения запроса.
Большой рабочий набор данных может одновременно создавать давление на TLB и кэш. Именно поэтому использование крупных страниц иногда помогает не потому, что сама DRAM становится быстрее, а потому что уменьшается количество переводов, которые нужно хранить в TLB. Крупные страницы могут снизить число обращений к структурам трансляции, но одновременно привести к внутренним потерям памяти и усложнить размещение.
Для эксперимента с программой полезно проверять:
- размер и структуру рабочих массивов;
- последовательный или случайный характер доступа;
- количество промахов TLB;
- промахи кэша разных уровней;
- задержки при NUMA-доступе;
- наличие swap и major page faults.
Иначе можно оптимизировать не тот слой. Например, увеличение размера кэша не исправит проблему, если процесс постоянно сталкивается с page faults из-за нехватки памяти.
Полный маршрут одной операции чтения
Теперь соберём процесс в одну последовательность.
Допустим, rdi содержит виртуальный адрес элемента массива, а инструкция mov rax, [rdi] должна прочитать восемь байт.
Шаг 1. Формирование адреса.
Исполнительный блок вычисляет эффективный виртуальный адрес из базового регистра, индекса и смещения. Программа не знает, в каком физическом кадре находится массив.
Шаг 2. Проверка TLB.
MMU выделяет номер виртуальной страницы и ищет соответствие. При попадании сразу получает физический кадр. Смещение сохраняется.
Шаг 3. Обход таблиц.
При TLB miss MMU обращается к многоуровневой структуре, корень которой связан с текущим адресным пространством. Если запись валидна и права подходят, строится физический адрес. Если нет — возникает page fault или ошибка доступа.
Шаг 4. Проверка кэшей.
Физический адрес используется для поиска строки данных в кэше. При попадании восемь байт передаются исполнительному блоку. DRAM в этом случае не участвует в конкретном чтении.
Шаг 5. Запрос контроллеру.
При cache miss запрос уходит к контроллеру памяти. Контроллер учитывает очередь, правила планирования, состояние открытых строк и доступность каналов.
Шаг 6. Отображение в DRAM.
Функция платформы преобразует физический адрес во внутренние компоненты памяти. Контроллер выбирает канал, ранг, банк, строку и столбец. Если нужная строка уже открыта в банке, ситуация отличается от конфликта, когда требуется закрыть одну строку и открыть другую.
Шаг 7. Возврат данных.
Полученная строка данных передаётся обратно, обычно заполняет кэш, а затем нужные восемь байт поступают в rax. Следующее обращение к соседним данным может быть быстрее благодаря пространственной локальности.
Что добавляет виртуализация
В виртуальной машине адресный путь может получить ещё один уровень. Гостевая программа формирует гостевой виртуальный адрес. Гостевая операционная система переводит его в гостевой физический адрес. Затем гипервизор или аппаратный механизм вложенной трансляции преобразует гостевой физический адрес в физический адрес хоста.
Упрощённо:
гостевой виртуальный адрес
↓
гостевой физический адрес
↓
физический адрес хоста
↓
координаты DRAM
На системах с EPT или аналогичной технологией таблицы второго уровня помогают аппаратно выполнять эту дополнительную трансляцию. При этом давление на TLB и стоимость промахов могут отличаться от ситуации на обычной физической машине.
Это важно при сравнении удалённой разработки, контейнеров и виртуальных машин. Две среды с одинаковым объёмом RAM могут показывать разную задержку памяти из-за особенностей NUMA, вложенной трансляции, конкуренции за кэш и планировщика памяти. Если вы сравниваете варианты для удалённой работы, обзор удалённых сред Kvmzen поможет сначала определить, какие свойства окружения нужно зафиксировать до измерений. При повторной проверке той же среды полезно заранее определить требования к доступу, лимитам памяти и воспроизводимости нагрузки; эти параметры следует фиксировать до начала теста.
Сравнение уровней адресации
| Уровень | Что видит программный код | Что определяется на этом этапе | Типичная ошибка интерпретации |
|---|---|---|---|
| Виртуальный адрес | Указатель, адрес инструкции или объект отладчика | Виртуальная страница и смещение | Считать его адресом микросхемы RAM |
| TLB | Аппаратный кэш перевода | Быстрое соответствие страницы физическому кадру | Принимать TLB miss за обращение к DRAM |
| Таблица страниц | Структура ядра и MMU | Физический кадр, права, наличие страницы | Считать любой page fault чтением с диска |
| Физический адрес | Адрес физического пространства | Запрос для контроллера памяти | Искать в нём готовые номера банка и строки |
| Кэш | Строка данных CPU | Попадание или промах по физическому адресу | Объяснять каждую задержку только скоростью RAM |
| DRAM-отображение | Внутренняя логика контроллера | Канал, ранг, банк, строка и столбец | Применять фиксированную битовую формулу ко всем платформам |
Как проверять проблему без догадок
Если вы расследуете медленный доступ к памяти, действуйте по слоям.
-
Зафиксируйте тип нагрузки.
Разделите последовательный доступ, случайные указатели, потоковую обработку и конкурентные обращения нескольких потоков. -
Проверьте адресное пространство процесса.
В Linux изучите отображения процесса через/proc/<pid>/mapsи характеристики памяти через/proc/<pid>/smaps. Это покажет виртуальные области, но не раскроет автоматически внутреннюю формулу DRAM. -
Отделите TLB-промахи от промахов кэша.
Используйте аппаратные счётчики производительности, доступные на вашей архитектуре. Названия событий и точность измерения зависят от процессора, поэтому сверяйте их с руководством производителя. -
Проверьте page faults.
Сопоставьте minor и major faults с активностью swap, диска и изменением рабочего набора. Если major faults растут, проблема уже выходит за пределы простой задержки DRAM. -
Сравните обычные и крупные страницы.
Экспериментируйте с Transparent Huge Pages или явно выделенными huge pages только на контролируемом стенде. Крупная страница может снизить нагрузку на TLB, но увеличить внутренние потери памяти и усложнить размещение. -
Проверьте NUMA и виртуализацию.
На многосокетных системах удалённый узел памяти может давать другую задержку. В виртуальной машине добавьте к анализу вложенную трансляцию и конкуренцию с другими гостями. -
Не пытайтесь вывести DRAM-формулу из одного адреса.
Для реконструкции банков и строк нужны повторяемые измерения, контроль частоты, состояния кэша, планировщика и фоновой нагрузки. Исследования по обратному восстановлению DRAM-отображений указывают на платформенную зависимость таких функций. (arXiv)
Таблица решений для диагностики
| Наблюдение | Вероятный слой проблемы | Что проверить первым | Какое решение не стоит применять сразу |
|---|---|---|---|
| Много TLB miss при большом рабочем наборе | Трансляция адресов | Размер страниц, структура указателей, huge pages | Без проверки не включать крупные страницы для всех процессов |
| Высокий cache miss при низком page fault | Локальность доступа | Шаг обхода массива, размер строки, размещение данных | Не менять тип RAM, пока не проверен алгоритм |
| Рост major page faults | Давление на виртуальную память | Swap, лимиты контейнера, рабочий набор | Не обвинять DRAM latency |
| Неровная задержка между узлами | NUMA или конкуренция | Размещение потока и памяти, загрузку соседних задач | Не усреднять все замеры в одно число |
| Разница между физической машиной и VM | Вложенная трансляция и планировщик | Huge pages, EPT/NPT, лимиты гостя | Не сравнивать только объём выделенной RAM |
| Разные результаты при смене платформы | Реализация контроллера и DRAM mapping | Документацию конкретной системы и повторяемость теста | Не использовать чужую формулу битового разбиения |
Что выбрать для практического эксперимента
| Сценарий | Подходящая среда | Почему | Ограничение |
|---|---|---|---|
| Изучение виртуального адреса и page fault | Локальная Linux-система или изолированная VM | Легко наблюдать /proc, карты памяти и события ядра |
VM добавляет второй уровень трансляции |
| Анализ TLB и кэша | Физическая машина с доступными счётчиками | Меньше неопределённости в аппаратном пути | Счётчики требуют корректной привязки к модели CPU |
| Исследование DRAM Bank и строк | Конкретная физическая платформа | Можно повторять измерения задержек | Нельзя переносить результат на другой контроллер |
| Проверка приложения в удалённой среде | Стабильный арендованный Mac или выделенный сервер | Удобно тестировать реальную среду разработки | Физическая топология памяти может быть скрыта |
| Долгий нагрузочный тест | Собственная машина или согласованный выделенный узел | Проще контролировать фоновые процессы и лимиты | Покупка оправдана только при постоянной нагрузке |
Если текущая среда — общий облачный сервер или VM, у неё могут быть три практических минуса: переменная конкуренция за CPU и кэш, скрытая NUMA-топология и дополнительный слой адресной трансляции. К этому иногда добавляются ограничения прав, контейнерные лимиты и непредсказуемый фоновой I/O. Для короткой проверки Mac-сборки, инструментов разработки или поведения Agent под нагрузкой аренда Mac у Kvmzen может дать более воспроизводимое окружение, чем случайный общий узел. Для постоянной тяжёлой нагрузки без пауз и для задач, которым нужны физические интерфейсы, разумнее рассмотреть собственное оборудование.
Частые вопросы
Как виртуальный адрес превращается в физический?
Сначала процессор выделяет номер виртуальной страницы и смещение внутри неё. MMU ищет готовое соответствие в TLB. При попадании номер физического кадра объединяется с тем же смещением. При промахе MMU обращается к многоуровневым таблицам страниц, проверяет права доступа и наличие страницы. Если данные отсутствуют, возникает исключение page fault.
Что происходит после промаха TLB?
Процессор не обращается сразу к DRAM-чипу. Сначала аппаратный механизм выполняет обход таблиц страниц или передаёт обработку ядру, если архитектура этого требует. Читаются записи верхних и нижних уровней, проверяются флаги доступа и строится новая запись TLB. После этого исходная инструкция обычно повторяется, если не возникла ошибка доступа или нехватка физической памяти.
Физический адрес — это адрес конкретной микросхемы RAM?
Нет. Физический адрес — это адрес в физическом адресном пространстве, видимый подсистеме памяти. Контроллер памяти дополнительно преобразует его в компоненты конкретной платформы: канал, ранг, банк, строку и столбец. Часть правил может быть скрыта в контроллере или прошивке, поэтому один и тот же шаблон битов нельзя безопасно переносить между разными системами.
Как определяется банк и строка DRAM?
Контроллер памяти применяет собственную функцию распределения битов физического адреса. В неё могут входить XOR-сочетания отдельных битов, чередование по каналам и правила выбора банков. Поэтому универсальной формулы нет. Для анализа конкретной машины используют документацию платформы, аппаратные счётчики и экспериментальные методы, например сравнение задержек при попадании и конфликте строк.
Главное, что нужно запомнить
CPU находит данные не по прямому маршруту «указатель → микросхема RAM». Реальная цепочка выглядит так:
виртуальный адрес
→ TLB или таблицы страниц
→ физический адрес
→ кэш
→ контроллер памяти
→ канал / ранг / банк / строка / столбец DRAM
Если вы оптимизируете программу, сначала определите, на каком уровне возникает задержка. TLB miss требует анализа трансляции и размера страниц. Cache miss указывает на проблему локальности или рабочего набора. Page fault может быть связан с виртуальной памятью и swap. А конфликт банков и строк DRAM можно исследовать только с учётом конкретной платформы.
Именно такое разделение не позволяет перепутать адрес процесса с физическим расположением памяти и выбрать неподходящее средство оптимизации.
Часто задаваемые вопросы
Как виртуальный адрес превращается в физический?
Сначала процессор выделяет номер виртуальной страницы и смещение внутри неё. MMU ищет готовое соответствие в TLB. При попадании номер физического кадра объединяется с тем же смещением. При промахе MMU обращается к многоуровневым таблицам страниц, проверяет права доступа и наличие страницы в памяти. Если данные отсутствуют, возникает исключение page fault.
Что происходит после промаха TLB?
Процессор не обращается сразу к DRAM-чипу. Сначала аппаратный механизм выполняет обход таблиц страниц или передаёт обработку ядру, если архитектура этого требует. Читаются записи верхних и нижних уровней, проверяются флаги доступа и строится новая запись TLB. После этого исходная инструкция обычно повторяется, если не возникла ошибка доступа или нехватка физической памяти.
Физический адрес — это адрес конкретной микросхемы RAM?
Нет. Физический адрес — это адрес в физическом адресном пространстве, видимый подсистеме памяти. Контроллер памяти дополнительно преобразует его в компоненты конкретной платформы: канал, ранг, банк, строку и столбец. Часть правил может быть скрыта в контроллере или прошивке, поэтому один и тот же шаблон битов нельзя безопасно переносить между разными системами.
Как определяется банк и строка DRAM?
Контроллер памяти применяет собственную функцию распределения битов физического адреса. В неё могут входить XOR-сочетания отдельных битов, чередование по каналам и правила выбора банков. Поэтому универсальной формулы нет. Для анализа конкретной машины используют документацию платформы, аппаратные счётчики и экспериментальные методы, например сравнение задержек при попадании и конфликте строк.
