Для начала, необходимо рассказать об основах физической и виртуальной
адресации. Физическое пространство включает в себя реальный размер установленной
оперативной памяти плюс адресное пространство шин PCI, а виртуальное есть
некоторое мнимое пространство, предназначенное для программных задач.
Фактически, виртуальное адресное пространство больше чем или равно физическому.
Каждая запущенная задача наделяется некоторой частью виртуальной памяти, которая
отображается на физическую память каким-либо способом, причём несколько
виртуальных адресов могут соответствовать одному физическому. Как виртуальное,
так и физическое пространство используют страницы памяти для нужд адресации.
Хотя оба типа страниц одинакового размера, но на самом деле размер физической
страницы зависит от размера строки микросхем памяти, которая отличается от
реализации к реализации, но это не имеет никакого значения с точки зрения обоих
адресных пространств. Чаще всего используются страницы размером в 4Кб и 8Кб,
хотя большие размеры могут также поддерживаться, такие как 2Мб или 4Мб. Хотя
техника страничной виртуальной адресации была изобретена не один десяток лет
назад, первым процессором архитектуры x86 с её поддержкой является Intel 386DX,
представленный в 1985, хотя основы виртуальной адресации были заложены в Intel
80286 ещё 3 года назад (защищённый режим, protected mode). Кстати, процессоры
Intel 8086 и 8088, которые дали жизнь всей архитектуре в 1978-79, поддерживали
только сегментную адресацию (реальный режим, real mode). В целом, этот режим
более ограничен и неудобен с программистской точки зрения.
Итак, существуют виртуальные и физические страницы памяти. Функциональные
устройства процессора работают с виртуальными адресами, а контроллерам
кэш-памяти и оперативной памяти приходится работать с адресами физическими.
Обязанности по преобразованию адресов возлагаются на A-box, хотя фактические
расчёты виртуальных адресов могут быть делегированы E-box. Операционная система
управляет виртуальной памятью и определяет способы её использования, хотя
аппаратные требования и органичения также налагаются. Обычно виртуальное
адресное пространство разделяется на две части: одна для нужд ядра операционной
системы и другая для пользовательских задач. Также имеются страничные таблицы
(page tables), которые служат для определения физического адреса страницы по
данному виртуальному. Например, 2-уровневая схема является наиболее
широкораспространённой. Она предусматривает наличие корневой страничной таблицы
(root page table) размером обычно в пределах одной страницы памяти, а также
большого числа пользовательских таблиц (user page tables). Каждая
пользовательская таблица обычно имеет размер в одну страницу памяти, а
максимальное число этих таблиц ограничено количеством записей в корневой
таблице. Таким образом, любая запись в корневой таблице адресует одну
пользовательскую таблицу, а любая запись в последней — одну страницу
физической памяти. Адрес виртуальной памяти содержит лишь смещения (offsets) и
совершенно бесполезен без страничных таблиц. Процесс получения физического
адреса страницы при помощи этих таблиц называется ходьбой по страничным таблицам
(page table walk). Все x86 процессоры, начиная с Intel 386DX, поддерживают
вышеописанную 2-уровневую схему для 4Кб страниц. Тем не менее, многие x86
процессоры, начиная с Intel Pentium, представленного в 1993, также включают
поддержку 4Мб страниц, которые упраздняют необходимость в пользовательских
таблицах, так как все физические страницы могут быть адресованы непосредственно
при помощи корневой таблицы. Более того, 4Кб и 4Мб страницы могут использоваться
одновременно, так как каждая запись в корневой таблице обладает битом размера
страницы (page size bit).
|
|
|
|
|
| R |
AVL |
PS |
A |
PCD |
PWT |
U/SW/R |
V |
D |
| Reserved |
Available to Software |
Page Size |
Accessed |
Page Cache Disable |
Page Write-Through |
User / Supervisor |
Write / Read |
Valid |
Dirty |
|
Однако, ходить по страничным таблицам при каждом запросе физического адреса
очень неэффективно. Следует изобрести некое подобие кэш-памяти для хранения
записей из страничных таблиц. Собственно, для этой цели и существуют буфера
преобразования (TLB — Translation Look-aside Buffers). По аналогии с
обычными кэшами, они могут быть разделёнными или унифицированными (I-TLB и D-TLB
против U-TLB), так что I-TLB хранит записи, относящиеся к страницам с командами,
а D-TLB — к страницам с данными. Уровней TLB может быть больше одного,
причём второй (S-TLB) обычно унифицирован. Разные TLB могут характеризоваться
разными ассоциативностями, количеством портов чтения/записи, политиками
замещения записей и т. д.
Существуют поддерживаемые аппаратно и программно буфера преобразования.
Первые предполагают наличие дополнительной процессорной логики для хождения по
таблицам страниц и заполнения буферов. Этот подход позволяет работать без
прерывания выполняемой задачи, в то время как при второй реализации этого не
избежать. Программные TLB поддерживаются средствами операционной системы. При
каждом промахе TLB генерируется соответствующий исключительная ситуация
(exception), которая обрабатывается операционной системой. Она сохраняет
состояние запущенной задачи, проходит по таблицам страниц, заполняет TLB и
восстанавливает состояние проистановленной задачи. Можно предположить, что
этот вариант уступает предыдущему в быстродействии, но на самом деле разница
почти незаметна, так как обработчик этой исключительной ситуации компактен и
хорошо кэшируется. С другой стороны, программная реализация предоставляет больше
свободы проектировщикам операционных систем. Кстати, хождение по страничным
таблицам совершенно необязательно должно быть всегда успешным. Если виртуальная
страница не имеет соответствующей ей физической страницы, генерируется
исключительная ситуация страничного сбоя (page fault) с последующей остановкой
выполняемого процесса.
Следует упомянуть, что хотя большинство кэшей физически тэгированы,
некоторые могут быть виртуально тэгированы. Это решение увеличивает
производительность, так как отсутствует необходимость в просмотре TLB, но
привносит серьёзную проблему, связанную с дублированием строк. Если две задачи
используют одну и ту же физическую строку, находящуюся под разными адресами в их
виртуальных пространствах, и одна из задач изменяет свою копию, то вторая задача
может оставить этот факт без должного внимания и продолжать использовать свою
устаревшую копию. Самым простым способом избежания этой проблемы является сброс
кэш-памяти (cache flush) при каждом переключении между задачами, но этот вариант
наименее приемлем с точки зрения быстродействия. Иначе можно снабдить каждую
строку дополнительным полем, известным как ASID (Address Space IDentifier) или
RID (Region IDentifier). Есть и другие решения этой проблемы. В целом,
виртуальное тэгирование часто применяется лишь по отношению к I-caches.