§ Адресация памяти
Первое, с чем сталкивается программа при переходе в защищённый режим – это совершенно другая система адресации памяти. Для начала, давайте вспомним, как это происходит в режиме реальных адресов – для обращения к памяти используется пара 16-разрядных регистров – сегментный регистр и смещение.В сегментном регистре находится адрес сегмента. Сегмент – это область памяти размером в 64 Кб, которая должна начинаться на границе параграфа или, другими словами, на 16-байтной границе, то есть сегмент может начинаться только по адресу 0, 16, 32, 48 и т.д. Адресное пространство процессора 8086 равно одному мегабайту – это адреса в диапазоне от 00000h до FFFFFh. Т.к. сегмент начинается по адресу, кратному 16 (10h), то возможные адреса начала сегмента будут 00000h, 00010h, 00020h, ... , FFFF0h. Как видите, информация содержится в старших четырёх шестнадцатеричных разрядах – их и хранят в сегментном регистре. Другими словами, можно взять значение из сегментного регистра, умножить его на 16 (т.е. на 10h) и вы получите адрес начала сегмента.
Для указания конкретного адреса внутри сегмента используется второй 16-разрядный регистр, так называемое смещение. Использование пары регистров сегмент:смещение обеспечивает доступ ко всему мегабайту адресного пространства.
Каждый раз, перед тем как процессор обратится к памяти по адресу, указанному в паре регистров сегмент:смещение, он вычисляет адрес памяти по следующей схеме:
физический_адрес = сегмент * 10h + смещение.Осталось заметить, что, вообще говоря, в режиме реальных адресов адресное пространство немного больше одного мегабайта, а именно:
1Мб + 64Кб – 16 байт.Такой адрес получается, если загрузить в пару регистров максимальные значения:
FFFF:FFFFПри этом адрес будет равен:
FFFFh * 10H + FFFFh = FFFF0h + FFFFh = 10FFEFh = 1114095Теперь давайте рассмотрим схему образования адреса в защищённом режиме, как это происходит в процессоре i386 (80386 – это базовая модель для любого Pentium-а).
Адрес памяти в 32-разрядном процессоре является также 32-разрядным. Это значит, что адресное пространство для такого процессора равно 4 Гб (232 байт).
Адресация памяти также производится через сегмент и смещение в сегменте, для чего используется пара регистров, но для описания сегмента используется больше информации, а именно:
- 32-разрядный адрес
- 20-разрядный предел сегмента (предел = размер – 1)
- Номер уровня привилегий сегмента
- Тип сегмента (код, стек, данные или системный объект)
- Дополнительные свойства, о которых подробнее будет сказано в других главах
В качестве смещения используется 16- или 32-разрядный регистр.
При обращении к памяти, процессор проверяет возможность доступа к сегменту по уровню привилегий, проверяет, не превысил ли адрес предел сегмента и можно ли обращаться к этому сегменту в данном случае (например, запрещена передача управления в сегмент, описывающий данные или стек). Если в результате проверки будет обнаружено нарушение какого-либо условия, то процессор сгенерирует исключение и тем самым обеспечит защиту.
Предел сегмента – это максимально допустимое смещение внутри него, таким образом, предел сегмента определяет его размер: размер_сегмента = предел_сегмента + 1
Обратите внимание, что значение предела – 20-разрядная величина, это значит, что максимальное значение предела равно 220 – 1. Процессор измеряет размер сегмента двумя типами величин: либо байтами, либо страницами. Страница – это блок памяти размером в 4Кб.
В описании сегмента можно указать, в каких единицах измеряется сегмент и тогда можно получить два типа сегментов с максимальными размерами:
- 1Мб ( 220 байт )
- 4Гб ( 220 страниц = 220 * 4Кб = 220 * 212 = 232 байт )
Значение предела сегмента может быть любым, от 0 до 220 – 1, гранулярность устанавливается по усмотрению программиста и может быть либо байтная, либо страничная. Всё это позволяет определять сегменты любого размера – от 0 байт до 4Гб.