13) Эффективный адрес и преобразование адресов.
Три адресных пространства: логическое, линейное и физическое. По сочетанию сегментации и страничной трансляции различают две модели памяти: 1.В сегментной модели памяти приложение использует несколько сегментов памяти (для кода, данных, стека). В этой модели приложение оперирует логическими адресами. 2. В плоской модели памяти приложению для всех целей выделяется единственный сегмент. В этой модели приложение оперирует линейными адресами. Логический адрес состоит из селектора сегмента Seg и эффективного адреса, называемого также смещением (offset). Логический адрес обозначается в форме Seg:Offset. Селектор сегмента хранится в старших 14 битах сегментного регистра (CS, DS, ES, SS, FS или GS), участвующего в адресации конкретного элемента памяти.Преобразование логического адреса в физический для 32-битных процессоров. Физический адрес памяти образуется после преобразования линейного адреса блоком страничной трансляции адресов. Блок страничной трансляции адресов позволяет использовать разрядность физического адреса, отличную от разрядности линейного адреса. В процессорах различных моделей соотношения разрядностей менялись: 1. В 386SX при 32-битном линейном адресе физический был 24-битным (до 16 Мбайт физически адресуемой памяти). 2. В большинстве 32-битных процессоров до 6-го поколения использовался 32-битный физический адрес (до 4 Гбайт физически адресуемой памяти). Формирование адреса памяти процессоров с 64-битным расширением рисунок.
14) Страничная трансляция адресов и виртуальная память
СТА.Механизм сегментации обеспечивает превосходную защиту, но он не очень удобен для реализации виртуальной памяти (подкачки). В дескрипторе сегмента есть бит присутствия, по нему процессор определяет, находится ли данный сегмент в физической памяти или он находится на внешнем запоминающем устройстве (на винчестере). В последнем случае генерируется исключение #11, обработчик которого может подгрузить сегмент в память. Неудобство заключается в том, что различные сегменты могут иметь различную длину. Этого можно избежать, если механизм подкачки реализовывать на основе страничного преобразования. Особенностью этого преобразования является то, что процессор в этом случае оперирует с блоками физической памяти равной длины (4Кбайт) — страницами. Страницы не имеют непосредственного отношения к логической структуре программы.Страничное преобразование действует только в защищенном режиме и включается установкой в 1 бита PG в регистре CR0. Под виртуализацией памяти понимается метод автоматического управления иерархической памятью. По своей сути виртуализация памяти представляет способ аппаратной и программной реализации концепции иерархической памяти. В рамках идеи виртуализации памяти ОП рассматривается как линейное пространство N адресов, называемое физическим пространством (ФП) памяти. Для задач, где требуется более чем N ячеек, предоставляется значительно большее пространство адресов (обычно равное общей емкости всех видов памяти), называемое виртуальным пространством (ВП). Страничная организация виртуальной памяти Страничная организация памяти служит целям преобразования виртуальных адресов в физические. Программа разбивается на части равной величины, называемые страницами. Размер страницы обычно выбирают в пределах 4 – 8 Кбайт (должен быть кратен емкости одного сектора магнитного диска). Виртуальное и физическое адресные пространства разбиваются на блоки размером в страницу. Блок ОП, соответствующий странице, часто называют страничным кадром или фреймом (page frame). Страницам виртуальной и физической памяти присваивают номера.
Система команд x86
Команда LEA вычисляет эффективный адрес (смещение) операнда-источника команды (который должен быть операндом в памяти) и сохраняет его в регистре — операнде-назначении команды. Атрибут размера операнда команды определяется выбранным регистром (16- или 32-битным). Атрибут размера адреса определяется атрибутом текущего кодового сегмента. Атрибуты размера адреса и размера операнда воздействуют на операцию, выполняемую командой LEA так, как это показано в таблице 6.65.
Таблица 6.65. Воздействие атрибутов размера операнда и размера адреса на действие команды LEA
Размер операнда
Размер адреса
Выполняемая операция
Вычисляется 16-битный эффективный адрес и сохраняется в 16-битном регистре-назначении
Вычисляется 32-битный эффективный адрес его младшие 16 бит сохраняются в 16-битном регистре-назначении
Вычисляется 16-битный эффективный адрес, затем знакорасширяется и сохраняется в 32-битном регистре-назначении
Вычисляется 32-битный эффективный адрес и сохраняется в 32-битном регистре-назначении
Операция:
IF OperandSize = 16 AND AddressSize = 16
DEST = EffectiveAddress(SRC); (* 16- битный адрес *)
ELSE IF OperandSize = 16 AND AddressSize = 32 THEN
temp = EffectiveAddress(SRC); (* 32- битный адрес *)
DEST = temp[0..15]; (* 16- битный адрес *)
ELSE IF OperandSize = 32 AND AddressSize = 16 THEN
temp = EffectiveAddress(SRC); (* 16- битный адрес *)
DEST = ZeroExtend(temp); (* 32- битный адрес *)
ELSE IF OperandSize = 32 AND AddressSize = 32 THEN
DEST = EffectiveAddress(SRC); (* 32- битный адрес *)
Особые ситуации защищенного режима:
#UD, если второй операнд не является операндом в памяти.
Особые ситуации режима реальной адресации:
#UD, если второй операнд не является операндом в памяти.
Особые ситуации режима V86:
#UD, если второй операнд не является операндом в памяти.
Замечание:
Различные ассемблеры могут по разному интерпретировать команду в зависимости от атрибута размера и символической ссылки второго операнда.
как получить эффективный адрес (В NASM без использования LEA) по отношению к какому либо сегменту если есть смещение
Мужики, назрел вот такой вот дилетантский вопрос в процессе изучения NASM ,но сначала начну с предыстории возникшей дилеммы.
Как мы знаем в asm адресация бывает косвенная и обычная тут всё прозрачно соответственно если у нас операнд в ячейке памяти то для того чтобы к нему обратиться например в команде mov пишем:
mov eax, [ адрес || указатель ]
или же если мы адрес закинули в регистр:
Далее каждый знает что у нас есть сегментные регистры возьмём к примеру ds. Обычно к сегментным регистрам данных привязывают сегменты с зарезервированными ячейками памяти либо с конкретным значением то есть в этот регистр помещается начальный адрес нашего сегмента и чтобы в дальнейшем обратиться какой либо его части адресного пространства мы используем смещение то есть смещаемся на какое то количество ячеек по отношению к базовому адресу но самое главное что для этого нам нужно знать только смещение и селектор а реальный адрес мы в голове не держим :
Итак тут то меня и смутило то что если я хочу получить этот эффективный адрес и закинуть его в регистр для дальнейших манипуляций NASM ругается на вот такую запись :
И это при том что если мы проинициализируем какую либо ячейку в памяти и присвоим ей метку а далее напишем вот так :
string db «ЛОЛ$»
mov eax,string
То он это проглотит да ещё и адрес этой метки кинет в наш регистр.
Так вот мой вопрос в том что почему инструкция mov eax, ds:0x81 не работает ведь по логике вещей должна
Адреса памяти: физические, виртуальные, логические, линейные, эффективные, гостевые
Мне периодически приходится объяснять разным людям некоторые аспекты архитектуры Intel® IA-32, в том числе замысловатость системы адресации данных в памяти, которая, похоже, реализовала почти все когда-то придуманные идеи. Я решил оформить развёрнутый ответ в этой статье. Надеюсь, что он будет полезен ещё кому-нибудь.
При исполнении машинных инструкций считываются и записываются данные, которые могут находиться в нескольких местах: в регистрах самого процессора, в виде констант, закодированных в инструкции, а также в оперативной памяти. Если данные находятся в памяти, то их положение определяется некоторым числом — адресом. По ряду причин, которые, я надеюсь, станут понятными в процессе чтения этой статьи, исходный адрес, закодированный в инструкции, проходит через несколько преобразований.
На рисунке — сегментация и страничное преобразование адреса, как они выглядели 27 лет назад. Иллюстрация из Intel 80386 Programmers’s Reference Manual 1986 года. Забавно, что в описании рисунка есть аж две опечатки: «80306 Addressing Machanism». В наше время адрес подвергается более сложным преобразованиям, а иллюстрации больше не делают в псевдографике.
Начнём немного с конца — с цели всей цепочки преобразований.
Физический адрес
Конечный результат всех преобразований других типов адресов, перечисленных далее в этой статье — физический адрес. На нём кончается работа внутри центрального процессора по преобразованию адресов.
Конечный результат?!
На самом деле, легко понять, что это ещё не конец. В платформе, которая должна обработать запрос данных от процессора, может быть несколько чипов DRAM, имеющих собственную структуру разбиения на блоки, а также различные периферийные устройства, отображённые на общее пространство физической памяти. Дальнейший путь транзакции с некоторым физическим адресом будет зависеть от конфигурации нескольких декодеров, находящихся на её пути внутри устройств платформы.
Эффективный адрес
Эффективный адрес — это начало пути. Он задаётся в аргументах индивидуальной машинной инструкции, и вычисляется из значений регистров, смещений и масштабирующих коэффициентов, заданных в ней явно или неявно.
Например, для инструкции (ассемблер в AT&T-нотации)
addl %eax, 0x11(%ebp, %edx, 8)
эффективный адрес операнда-назначения будет вычислен по формуле:
eff_addr = EBP + EDX * 8 + 0x11
Логический адрес
Без знания номера и параметров сегмента, в котором указан эффективный адрес, последний бесполезен. Сам сегмент выбирается ещё одним числом, именуемым селектором. Пара чисел, записываемая как selector:offset , получила имя логический адрес. Так как активные селекторы хранятся в группе специальных регистров, чаще всего вместо первого числа в паре записывается имя регистра, например, ds:0x11223344.
Здесь обычно у тех, кто столкнулся с этими понятиями впервые, голова начинает идти кругом. Несколько упростить (или усложнить) ситуацию помогает тот факт, что почти всегда выбор селектора (и связанного с ним сегмента) делается исходя из «смысла» доступа. По умолчанию, если в кодировке машинной инструкции не сказано иного, для получения адресов кода используются логические адреса с селектором CS, для данных — с DS, для стека — с SS.
Линейный адрес
Эффективный адрес — это смещение от начала сегмента — его базы. Если сложить базу и эффективный адрес, то получим число, называемое линейным адресом:
lin_addr = segment.base + eff_addr
Преобразование логический → линейный не всегда может быть успешным, так как при его исполнении проверяется несколько условий на свойства сегмента, записанных в полях его дескриптора. Например, проверяется выход за границы сегмента и права доступа.
Реальный режим
Описанное выше верно при включенной сегментации. В 16-битном реальном режиме смысл селекторов другой, они хранят только базу, а преобразование не осуществляет сегментных проверок. Фактически, обозначения CS, DS, FS, GS, ES, SS имеют совершенно разный смысл в этих двух режимах, что добавляет путаницы.
Сегментация была модной на некотором этапе развития вычислительной техники. В настоящее она почти всюду была заменена другими механизмами, и используется только для специфических задач. Так, в режиме IA-32e (64-битном) только два сегмента могут иметь ненулевую базу. Для остальных четырёх в этом режиме всегда линейный адрес == эффективный.
Что такое виртуальный адрес?
В литературе и в документации других архитектур встречается ещё один термин — виртуальный адрес. Он не используется в документации Intel на IA-32, однако встречается, например, в описании Intel® Itanium, в котором сегментация не используется. Можно смело считать, что для IA-32 виртуальный == линейный.
В советской литературе по вычислительной технике этот вид адресов также именовался математическим.
Страничное преобразование
Следующее после сегментации преобразование адресов: линейный → физический — имеет множество вариаций в своём алгоритме, в зависимости от того, в каком режиме (32-битном, PAE или 64-битном) находится процессор.
Что влияет на paging
Примечательно, сколько различных бит из разных системных регистров процессора влияют на процесс страничного преобразования в настоящее время. Я просмотрел свежую сентябрьскую редакцию Intel SDM [1], и вот полный список: CR0.WP, CR0.PG, CR4.PSE, CR4.PAE, CR4.PGE, CR4.PCIDE, CR4.SMEP, CR4.SMAP, IA32_EFER.LME, IA32_EFER.NXE, EFLAGS.AC.
Однако общая идея всегда одна и та же: линейный адрес разбивается на несколько частей, каждая из которых служит индексом в одной из системных таблиц, хранящихся в памяти. Записи в таблицах — это адреса начала таблицы следующего уровня или, для последнего уровня — искомая информация о физическом адресе страницы в памяти и её свойствах. Самые младшие биты не преобразуются, а используются для адресации внутри найденной страницы. Например, для режима PAE с размером страниц 4 кбайт преобразование выглядит так:
В разных режимах процессора различается число и ёмкость этих таблиц. Преобразование может завершиться неудачей, если очередная таблица не содержит валидных данных, или права доступа, хранящиеся в последней из них, запрещают доступ к странице; например, при записи в регионы, помеченные как «только для чтения», или попытке чтения памяти ядра из непривилегированного процесса.
Гостевой физический
До введения возможностей аппаратной виртуализации в процессорах Intel страничное преобразование было последним в цепочке. Когда же на одной системе работают несколько виртуальных машин, то физические адреса, получаемые в каждой из них, приходится транслировать ещё один раз. Это можно делать программным образом, или же аппаратно, если процессор поддерживает функциональность EPT (англ. Extended Page Table). Адрес, раньше называвшийся физическим, был переименован в гостевой физический для того, чтобы отличать его от настоящего физического. Они связаны с помощью EPT-преобразования. Алгоритм последнего схож с ранее описанным страничным преобразованием: набор связанных таблиц с общим корнем, последний уровень которых определяет, существует ли физическая страница для указанной гостевой физической.
Полная картина
Я попытался собрать все преобразования адреса в одну иллюстрацию. В ней преобразования обозначены стрелками, типы адресов обведены в рамки.
Как уже было сказано выше, каждое из преобразований может вернуть ошибку для адресов, не имеющих представления в следующем по цепочке виде. Устранение подобных проблем — это задача операционных систем и мониторов виртуальных машин, реализующих абстракцию виртуальной памяти.
Заключение
Эволюция, что в природе, что в технике — странная вещь. Она порождает неожиданные структуры, необъяснимые с точки зрения рационального проектирования. Её творения полны атавизмов, правила их поведения иногда почти полностью состоят из исключений. Для того, чтобы понять работу такой системы, часто требуется прокрутить её эволюцию с самого начала, и под нагромождениями всех слоёв найти истину в виде принципа: «ничего не выбрасывать». Я склонен считать архитектуру IA-32 замечательным примером эволюционного развития.
P.S. Всё как у всех
Вскоре после завершения написания этой статьи я натолкнулся на презентацию об архитектуре IBM System z, которая примечательна в том числе своей долгой и интересной историей поддержки виртуализации. В этом документе нашлось перечисление всех типов адресов памяти, используемых в System z:
- Virtual: Translated by dynamic address translation (DAT) to real addresses
- Real: Translated to absolute addresses using the prefix register
- Absolute: After applying the prefix register
- Logical: The address seen by the program (this can either be a virtual or a real address)
- Physical: translated to absolute addresses by the Config Array
Как можно заметить, их тоже пять.
Спасибо за внимание!
Литература
- Intel Corporation. Intel® 64 and IA-32 Architectures Software Developer’s Manual. Volumes 1–3, 2014. www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html