Адресация физической памяти в процессорах Intel 80х86 и совместимых

2000, 2007, 30.11.2017

В статье "Простые типы данных" мы разобрались, как, и какие, операции выполняются процессором, какие типы машинных данных существуют. Это была первая ступенька в сложный и запутанный, но захватывающе интересный мир системного программирования. Теперь сделаем второй шаг, разберемся, как процессор получает доступ к данным. В этой статье будут рассматриваться только процессоры Intel 80х86 и совместимые с ними. Кроме того, статья была написана в далеком 2000 году и в ней рассматриваются только процессоры существовавшие на тот момент.

Прежде, чем вплотную заняться вопросами адресации, надо разобраться с режимами работы процессоров Intel 80x86. Самый старый процессор семейства, 8086, мог работать только в одном режиме. Следующая модель, процессор 80286, сохранил режим совместимости с 8086. Этот режим получил название реального режима, или R-режима. Дополнительный режим, обеспечивающий защиту памяти, многозадачность, более гибкое управление ресурсами, получил название виртуального режима. Однако этот режим не позволял скрыть от программ факт выполнения на процессоре, отличном от 8086, и заставлял писать все программы с учетом своих новых особенностей. Процессор 80386 стал первым 32 разрядным процессором семейства. Кроме увеличения разрядности этот процессор получил возможность работать еще в одном режиме, режиме эмуляции 8086. Правда с названиями режимов произошла небольшая путаница. Режим совместимости по прежнему называли R-режимом, однако виртуальным режимом, или V-режимом, стали называть режим эмуляции 8086. Режим расширенных возможностей, называющийся ранее виртуальным, стали называть защищенным режимом, или P-режимом. V-режим позволил решить проблему написания программ, не интересующихся новыми возможностями. Однако операционная система получила возможность полного контроля над такими программами. То есть концепция виртуальной машины стала поддерживаться на уровне процессора. Например, стал реальностью одновременный запуск двух различных версий MS-DOS, причем обе будут считать, что выполняются в полном одиночестве. Процессоры последующих моделей (до Pentium) ничего существенно нового не привнесли. Повышение скорости работы, изменение на аппаратном уровне, и даже добавление набора команд ММХ практически не отразилось на программной модели процессора. Названия режимов работы процессоров, которые мы будем использовать, соответствуют названиям режимов работы процессора 80386.

Структура оперативной памяти

Оперативная память состоит из отдельных байт, причем каждому байту назначен уникальный адрес (беззнаковое число). Адреса начинаются с 0, а последний доступный адрес определяется моделью используемого процессора и режимом его работы. Например, для процессора 8086, последним доступным адресом будет FFFFF. Любые два смежных байта образуют слово. За адрес слова принимается адрес его младшего байта. Для 32 разрядных моделей, любые четыре смежных байта образуют двойное слово. За адрес двойного слова принимается адрес его младшего слова, а следовательно, адрес его младшего байта. Байты, слова и двойные слова могут располагаться с любого адреса. Однако для слов и двойных слов существует понятие выравнивания. Операции со словами, например, будут выполняться быстрее, если слово начинается с адреса, кратного 2, или, по другому, с четного адреса. В некоторых случаях выравнивание обязательно. Иногда требуется выравнивание по адресу кратному 4, 8, 16, или даже 4096.

Структура оперативной памяти

Кроме того, с точки зрения программиста, память разделена на сегменты. Это вызвано тем, что процессор 8086 оперирует 16 разрядными числами, следовательно, адресуемая память составляет 216=65536 байт (с 0 до 65535), чего явно не достаточно. Поэтому адресная шина процессора состоит из 20 разрядов (для 8086), что позволяет адресовать 1М байт. Сегментная организация и позволяет отобразить 16 разрядный адрес процессора в 20 разрядный (для 8086) адрес памяти. Более того, такая организация позволяет легко писать программы, которые можно перемещать по памяти. При этом требуется только поменять содержимое сегментного регистра, а саму программу можно оставить не измененной. Это называется позиционная независимость. Современные модели процессоров работают с 32 разрядными, или даже 64 разрядными, адресами. Но для совместимости сверху вниз, с процессором 8086, сегментная схема адресации в них сохранена.

Регистры

Кроме оперативной памяти, внешней по отношению к процессору, существует память расположенная непосредственно в процессоре. Это регистровая память. А отдельные ее ячейки называются регистрами. Как правило, регистры не имеют собственных адресов. Для доступа к ним используются специальные форматы машинных команд. В программах на языке Ассемблера регистры обозначают символическими именами. Такое же наименование принято и при описании аппаратной части и архитектуры процессоров.

Важно отметить, что основным отличием регистра от ячейки памяти, кроме расположения, является использование его для хранения управляющей информации, а не произвольных данных (единственное исключение составляют регистры общего назначения). Регистровая память имеется не только в процессоре, но и периферийных устройствах и контроллерах. Ими ведь тоже надо управлять. Причем контроллеры периферийных устройств могут иметь не только регистры, но и собственную память. Например контроллер дисплея имеет и регистры (регистр режима, регистр битовой маски и т.д.) и оперативную память, называемую видеопамятью.

Процессоры Intel 80х86 имеют несколько групп регистров:

  • Регистры общего назначения. AL, AH, AX, EAX, BL, BH, BX, EBX, CL, CH, CX, ECX, DL, DH, DX, EDX. Эти регистры используются для промежуточного хранения результатов операций. Некоторые регистры общего назначения имеют и специальное назначение. Например, регистры CL, CX и ECX используются как счетчики в командах сдвигов.

  • Указательные и индексные регистры. SP - указатель стека, BP - указатель базы, SI - индекс источника, DI - индекс приемника. Эти регистры могут использоваться как явным, так и не явным образом. Например регистр SI можно использовать и как регистр общего назначения, для хранения промежуточных результатов, и как специализированный регистр, для последовательного доступа к символам в символьной строке. Регистр SP автоматически используется в операциях со стеком, в том числе при вызове процедур.

  • Сегментные регистры. CS - сегмент кода программы, DS - сегмент данных, SS - сегмент стека, ES - сегмент дополнительных данных. Начиная с процессора 80386 добавились еще два сегментных регистра, FS и GS. Их назначение такое же, как и регистра ES. Эти регистры используются для хранения сегментной части логического адреса.

  • Указатель команды (счетчик команд). Регистр IP всегда содержит адрес следующей выполняемой машинной команды.

  • Регистр флажков и слова состояния процессора. Этот регистр называют по разному: PSW - слово состояния процессора, FLAGS - регистр флагов, EFLAGS - расширенный регистр флагов. Суть от названия не меняется. Он содержит признаки результата и другую информацию о состоянии процессора. Кстати, в программах на Ассемблере его всегда называют PSW.

  • Регистры специального назначения, системные и управляющие регистры. Как следует из названия, в эту группу входят регистры, используемые, в основном, системными программистами и операционными системами. Они определяют расположение в оперативной памяти системных таблиц, режимов работы процессора, функционирование механизмов защиты, содержат дополнительную информацию о текущем состоянии процессора. Обращаться к этим регистрам можно только в том случае, если Вы совершенно четко представляете себе назначение регистра и последствия обращения. В противном случае последствия не предсказуемы. Нет, до физической поломки компьютера дело не дойдет, но вот потеря данных и зависание более чем вероятны. Не буду перечислять все регистры этой группы. Тем более, что каждая новая модель процессора расширяет состав группы за счет новых регистров или использования ранее зарезервированных битов существующих регистров. С назначением некоторых системных регистров Вы познакомитесь в дальнейшем, при рассмотрении схем преобразования адресов. Другие регистры будут рассматриваться в статьях посвященных тонкостям защищенного режима и многозадачности.

Сегментная адресация памяти в процессоре 8086 и в R-режимах процессоров старших моделей

Сегментом называется блок смежных адресов. Для процессора 8086 сегменты имеют размер 64К байт, или 65536 байт (с 0 до 65535). Разумеется, сегмент может быть и меньшего размера, однако контроль за этим возлагается на программиста. Процессор не контролирует размер сегмента и не отслеживает выход за его границы. Более того, к одному и тому же байту физической памяти можно получить доступ используя разные логические адреса (адреса в программе). Эти ситуации могут привести к серьезным ошибкам, поэтому процессоры старших моделей, начиная с 80286, имеют средства для более гибкого управления сегментами.

При сегментной организации памяти логический адрес (адрес в программе или в процессоре) занимает 32 бита. При этом он состоит из двух 16 разрядных компонент. Первая называется сегментным адресом, и определяет адрес начала сегмента в физической памяти. Вторая называется смещением в сегменте, или просто смещением. Она определяет адрес байта в пределах сегмента. Физический адрес занимает 20 разрядов. Это верно даже для Pentium Pro, если он работает в R-режиме. Преобразование логического адреса в физический (адрес в оперативной памяти) выполняется по очень простой схеме: сегментный адрес сдвигается влево на 4 разряда, после чего к нему прибавляется смещение. Возможный перенос из старшего разряда при сложении игнорируется. Обратите внимание, что в преобразовании логического адреса в физический неявно используется сегментный регистр. CS для получения следующей выполняемой команды, SS для доступа к стеку, DS для доступа к данным. Регистр ES неявно используется только в случае цепочечных команд, в других случаях его надо указывать явно. Регистры FS и GS всегда требуют явного указания.

Преобразование адресов при сегментной адресации

Логический адрес, состоящий из сегмента и смещения обычно записывают в виде сегмент:смещение. Например адрес из примера можно записать так - FF52:FFF4. Результирующий физический адрес будет 0F514, так как перенос игнорируется. Логический адрес FFFF:FFFF отображается на 0FFEF, то есть 64К минус 16 байт. Однако тут есть одна тонкость. Процессор 80386 и последующие не выполняют игнорирование переноса в R-режиме. Объясняется это очень просто, физический адрес в этих процессорах занимает 32 бита, а от усложнения схемы решили отказаться. Хотя некоторые считают, что в 80386 просто была допущена ошибка, а в последующих моделях ее повторили из-за совместимости. Как бы то ни было, это позволяет на процессорах старших моделей адресовать более 1М в R-режиме. Точнее 1М и еще 64К без 16 байт. Но не все программы могут работать без игнорирования переноса. Выход был найден простой и элегантный. Некоторые наверно слышали про шину, или линию A20. Для адресации 1М памяти требуется 20 адресных линий, с А0 по А19. Адреса с 100000 по 10FFEF, лежащие выше 1М, требуют еще одной адресной линии - А20. Следовательно, если запретить эту линию, переполнение будет игнорироваться, а если разрешить, то память свыше 1М будет доступна и в R-режиме. При работе в V или P режимах линия А20 должна быть открыта. Подробности управления линией А20 будут рассматриваться в отдельной статье. Кстати, управлением линией А20 занимался драйвер himem.sys в MS-DOS. В Widnows 3.x, в расширенном режиме, линией А20 управлял драйвер WinA20.386.

Сегментная адресация памяти в V-режиме процессора 80286 и в P-режиме процессоров последующих моделей.

Как уже говорилось, простейшая схема преобразования логических адресов в физические не исключает ошибок выхода за пределы сегмента и не обеспечивает защиты памяти. Злонамеренная программа запросто может записать данные в области памяти, принадлежащие операционной системе. Или, записав в область данных код вируса, передать ему управление. В многозадачных системах часто требуется перемещение программ в памяти или их выгрузка на диск, для освобождения места более высокоприоритетной задаче. Обратная загрузка программы в память может выполняться совсем по другим адресам. Простейшая сегментная адресация не в состоянии решить возникающих при этом проблем.

Посмотрите еще раз, внимательно, на схему преобразования адресов R режима. Сегментная часть адреса напрямую участвует в формировании физического адреса. Если программа использует только по одному сегменту кода, данных и стека, не перезагружая в процессе своей работы сегментные регистры, то можно переместить в физической памяти сегмент (например данных), изменив при этом значение соответствующего сегментного регистра. Программа будет продолжать нормально работать. Однако на практике программы имеют несколько сегментов кода и данных, при этом перезагрузка сегментных регистров выполняется неоднократно, и в произвольных местах программы. В этом случае операционная система не может просто переместить сегмент, нужно еще найти все места в программе, где выполняется загрузка сегментного регистра, и изменить код. Однако это не всегда возможно. Программы реального режима часто вычисляют адрес (в том числе и сегментную часть) на лету. Есть программы формирующие свой код в процессе выполнения, это так называемые самомодифицирующиеся программы. В таких случаях операционная система будет бессильна.

Первое, что приходит на ум для решения этой проблемы, исключить непосредственное участие сегментной части адреса в формировании физического адреса. Использовать что-то вроде косвенной адресации. Пусть сегментный регистр содержит, например, номер сегмента. Адрес начала сегмента в физической памяти можно будет брать из соответствующей строки системной таблицы. Если программы будут оперировать только теми сегментами, что запрашивали у операционной системы, то после перемещения сегмента достаточно изменить значение адреса начала сегмента в таблице. Программа будет продолжать использовать номер сегмента, он ведь остался неизменным. Использование только сегментов выделенных операционной системой, безусловно, ограничение. Однако плюсов в таком решении гораздо больше, чем минусов. Можно ведь развивать идею системной таблицы, например, для защиты сегментов. Решение, реализованное фирмой Intel, базируется как раз на использовании косвенной адресации и системных таблиц.

Первая попытка введения новой схемы адресации была предпринята при разработке процессора 80286. Она позволила обеспечить защиту областей памяти, принадлежащих разным программам. Защиту операционной системы. Перемещение программ в памяти стало простым и легким. Однако ограничения остались. Например реализация концепции виртуальной памяти, при которой на диск выгружается не вся программа, а лишь ее часть, затруднена различной длиной сегментов.

Реализация сегментной адресации в Р-режимах основана на введении промежуточного слоя обработки сегментной части адреса. Этот слой изолирует программу от подробностей размещения сегментов в физической памяти и обеспечивает процессор информацией, необходимой для защиты и управления сегментами. Кроме сегментных регистров, в P и V режимах, в преобразовании адреса участвуют регистры GDTR, LDTR и две дескрипторные таблицы.

Сегментная часть адреса, при работе в Р-режиме, называется селектором. Он состоит из трех полей: Index, TI, RPL. Поле Index является точкой входа (смещение в байтах от начала) в таблице, содержащей информацию о расположении сегмента в физической памяти и его атрибутах защиты. Каждая строка в такой таблице называется дескриптором, а сама таблица дескрипторной. Существует одна глобальная дескрипторная таблица, содержащая информацию для всех задач в системе. Кроме того, каждая задача, а все процессоры, начиная с 80286 поддерживают концепцию задачи, может иметь свою дескрипторную таблицу, называемую локальной. Формат обоих таблиц абсолютно одинаковый. Для выбора конкретной таблицы, глобальной или локальной, и служит поле TI. Поле RPL содержит так называемый запрашиваемый уровень привилегий. Всего имеется четыре таких уровня. Уровень 0 самый привилегированный и предназначен для ядра операционной системы. Уровень 3 самый не привилегированный и предназначен для прикладных программ. Уровни привилегий тесно связаны с понятием задач и защитой и будут рассмотрены в отдельной статье. Сейчас достаточно знать, что если менее привилегированная задача пытается получить доступ к более привилегированному сегменту, то запрос будет отвергнут.

Формат селектора адреса

Формат селектора адреса определяется разрядностью сегментных регистров. А поскольку даже процессор Pentium II имеет 16 разрядные сегментные регистры, то формат селектора идентичен для всех моделей процессоров, начиная с 80286. Размещение полей TI и RPL в младших разрядах селектора несколько усложняет адресную арифметику. В преобразовании логического адреса в физический участвую поля Index и TI. Поле RPL используется только для защиты памяти.

Дескриптор сегмента занимает 64 бита, это верно для любой модели процессора. А вот формат различен. Это объясняется тем, что процессор 80286 имеет 16 разрядную архитектуру и 24 разрядную шину адреса памяти, а начиная с 80386 процессоры стали 32 разрядными. Причем и шина адреса памяти стала 32 разрядной.

Формат дескриптора сегмента

Как видно из рисунка, формат дескриптора сегмента достаточно сложен и запутан. Но только на первый взгляд. Попробуем разобраться. Дескриптор состоит из трех основных частей: базового адреса сегмента, предела сегмента и прав доступа с параметрами. В процессоре 80286 биты начиная с 48 и по 63 не используются и должны быть обнулены.

Поле базового адреса выполняет роль, аналогичную сегментной части адреса в R режиме, то есть определяет начало расположения сегмента в физической памяти. Для процессора 80286 база занимает 24 бита. Для процессоров начиная с 80386 - 32 бита.

Поле предела определяет размер сегмента. Если смещение в сегменте будет равно значению поля предела, или превысит его, то доступ будет блокирован. Таким образом исключается случайный или намеренный выход за границы сегмента. Для процессора 80286 поле предела занимает 16 бит. Для процессоров начиная с 80386 - 24 бита.

Права доступа и параметры более сложны для понимания. Но и тут нет ничего таинственного и непостижимого.

  • Поле TYPE. Определяет тип сегмента:

    000 - сегмент данных, только чтение
    001 - сегмент данных, чтение/запись
    010 - сегмент стека, только чтение (не используется)
    011 - сегмент стека, чтение/запись
    100 - сегмент кода, только выполнение
    101 - сегмент кода, чтение/выполнение
    110 - подчиненный сегмент кода, только выполнение
    111 - подчиненный сегмент кода, чтение/выполнение
  • Поле S. Содержит 1 для сегментов памяти и 0 для системных объектов. Системный объект может являться, а может и не являться, сегментом памяти. Например, поле TYPE=001, если S=1, то это дескриптор сегмента данных с разрешенными записью и считыванием, а если S=0, то это дескриптор локальной таблицы дескрипторов LDT.

  • Поле DPL. Содержит уровень привилегий дескриптора. Кратко уровни привилегий упомянуты при описании селектора адреса. Более подробное обсуждение этих уровней выходит за рамки этой статьи и будет описано отдельно.

  • Поле G. Описывает так называемую гранулярность сегмента. Влияет на интерпретацию поля предела. Если G=0, то сегмент имеет байтную гранулярность, то есть поле предела задает размер сегмента в байтах. Если G=1, то сегмент имеет страничную гранулярность, то есть поле предела задает размер сегмента в страницах по 4К байт. Подробности страничной адресации будут рассмотрены чуть позже. Обратите внимание, процессор 80286 не имеет этого поля в дескрипторе.

  • Поле D. Задает разрядность сегмента. Если D=0, то сегмент содержит 16 разрядные данные. Если D=1, то сегмент соержит 32 разрядные данные. Собственно это поле определяет совместимость P режимов процессоров старших моделей с V режимом процессора 80286. Обратите внимание, процессор 80286 не содержит этого поля в дескрипторе.

  • Поле P. Бит присутствия. Помогает операционной системе организовать виртуальную память. Если P=0, то при обращении к сегменту возникает прерывание. Таким образом, операционной системе достаточно выгрузить сегмент на диск и установить P=0 в дескрипторе, сам дескриптор уничтожать не требуется. При обращении к сегменту находящемуся на диске, операционная система загружает его, возможно по другому физическому адресу, корректирует поле базового адреса и устанавливает P=1. После чего задача может продолжить выполнение, даже не подозревая о происшедшем.

  • Поле A. Бит обращения. Так же помогает операционной системе организовать виртуальную память. Процессор устанавливает этот бит при обращении к сегменту, описываемому данным дескриптором. Операционная система может периодически сбрасывать этот бит, а при нехватке памяти выгружать на диск те сегменты, для которых не было обращения, то есть этот бит остался 0.

  • Поле U. Пользовательский бит. Может использоваться системными программистами и операционными системами по своему усмотрению. Процессор 80286 не имеет этого поля в дескрипторе.

  • Поле X. Зарезервировано фирмой Intel и должно быть 0.

Как видим, ничего особенно сложного здесь нет. В преобразовании логического адреса в физический участвует только поле базового адреса. Остальные поля используются для защиты памяти и организации виртуальной памяти. Они упомянуты здесь для полноты картины.

Форматы селектора адреса и дескриптора сегмента мы рассмотрели. Для понимания процесса преобразования адресов осталось разобраться, где находятся глобальная (GDT) и локальная (LDT) дескрипторные таблицы. Ключём, или отправной точкой, здесь служит GDT местоположение которой задается регистром GDTR.

Формат регистра GDTR

Поле базового адреса занимает 24 бита, для процессора 80286, или 32 бита, для процессоров начиная с 80386. Поле предела содержит размер глобальной таблицы в байтах.

Местоположение локальной дескрипторной таблицы задается дескриптором, расположенным в глобальной таблице. Соответственно и регистр LDTR занимает только 16 бит. Фактически он является селектором адреса LDT.

Все. Осталось познакомиться с собственно схемой преобразования адреса. Она очень похожа на соответствующую схему R режима, если заменить сдвиг влево на 4 разряда сегментной части адреса на получение базового адреса из дескрипторных таблиц.

Адресация памяти в защищенном режиме

Подведем итоги. При сегментной адресации в Р режиме сегментная часть логического адреса не участвует напрямую в формировании физического адреса. Она используется для извлечения информации о сегменте из системных таблиц, которые содержат сведения о размещении сегмента в физической памяти и атрибутах его защиты. Использование системных таблиц позволяет решить практически все проблемы, возникавшие при попытках реализации многозадачных систем и систем виртуальной памяти для процессора 8086. При этом на программы накладываются некоторые ограничения, связанные, в основном, с обеспечением безопасности.

Страничная адресация памяти в P режиме процессоров начиная с 80386

Слой косвенной адресации, добавленный в преобразование сегментной части логического адреса, безусловно огромный шаг вперед. Именно начиная с процессора 80286 появилась возможность создания многозадачных и многопользовательских операционных систем для персональных компьютеров. РС из игрушек стали превращаться в полноценные рабочие станции и серверы. Однако для процессора 80286 так и не было создано ни одной полноценной многозадачной операционной системы. V режим этого процессора конечно использовался, например в Windows, но полноценных систем не было. В чем же тут дело?

Причин несколько. Рассмотрим четыре наиболее важные.

  1. Программам, чаще всего, требуются небольшие блоки памяти различной длины. Однако сегментная модель распределения памяти ориентирована на выделение целых сегментов. В системе может быть не более 8192 сегментов адресуемых через GDT, и по 8192 сегментов на каждую задачу (адресуются через LDT). Если учесть, что часто требуется использовать несколько дескрипторов для адресации одного сегмента, например для записи в сегмент кода, то становится ясно, что реализация больших систем наталкивается на ограниченность ресурсов.

  2. Реализация виртуальной памяти затруднена различной длиной сегментов. Это усложняет управление файлом подкачки (swap). Невозможно выгрузить на диск, или переместить в памяти часть сегмента.

  3. Процессор 80286 не может перейти из V режима в R режим. Фирма Intel решила, что такой переход никому не потребуется. В результате, производители материнских плат вынуждены были решать эту проблему внешними схемами.

  4. Использование V режима процессора 80286 не позволяло запускать программы написаные для 8086. Переписывать все существующие программы ни у кого особого желания не возникало.

Эти проблемы были решены в процессоре 80386. Насколько удачно, тема для отдельного разговора. Выбирать как говорится не приходится. Что же такого нового появилось в процессоре 80386, что позволило ему фактически совершить революцию в операционных системах персональных компьютеров?

Во первых, логический адрес стал 48 разрядным. 16 разрядов сегмент и 32 разряда смещение. 32 разряда смещения позволяют непосредственно адресовать 4Г памяти. То есть стало возможным вообще забыть про сегментные регистры. Так и делают, модель памяти, где не требуется управлять содержимым сегментных регистров называется плоской (FLAT). Она в точности совпадает с крошечной моделью (TINY), с той лишь разницей, что адресное пространство составляет не 64К байт, а 4Г байт.

Примечание. 32 разрядное смещение используется только в Р режиме работы процессоров начиная с 80386. В R режиме и для задач V режима смещение по прежнему 16 разрядное. Процессор просто отсекает старшие 16 бит смещения. Более того, при использовании в команде 32 разрядных операндов требует задания префикса перед командой, если Ваша программа работает в R режиме. Указать процессору, работающему в R режиме, что сегмент 32 разрядный невозможно. Сделано это для совместимости в процессором 8086. Однако есть обходные пути. Драйвер himem.sys из MS-DOS позволяет адресовать память за пределами 1М байт. При этом он НЕ использует Р режим! Объяснение простое, для ускорения выполнения программ в Р режиме используются специальные регистры процессора (теневые регистры), недоступные для программиста. В этих регистрах хранятся дескрипторы сегментов из дескрипторных таблиц, благодаря чему не требуется обращение к этим таблицам при выполнении каждой команды. Это позволяет существенно повысить быстродействие. Однако эти теневые регистры работают и в R режиме! Только в них занесены специальные значения, во все поля, кроме поля базового адреса. В частности, поле предела установлено в 64К байт, а бит D, указывающий разрядность, сброшен, что означает 16 разрядные сегменты. Когда программа R режима загружает сегментные регистры процессор обновляет поле базового адреса в теневых регистрах. В сегментном преобразовании участвуют именно эти теневые регистры! Значит, если удастся заменить поле базового адреса в теневых регистрах, можно будет обращаться к любым адресам памяти, независимо от того, какое значение находится в сегментных регистрах. Это можно сделать с помощью недокументированной команды loadall, которая загружает ВСЕ регистры процессора, в том числе теневые и системные. Именно эту команду, изначально предназначенную для тестирования процессоров, и использует драйвер himem.sys. Формат команды и выполняемые ею действия для каждого типа процессора разные, поэтому использовать ее в прикладных программах ОЧЕНЬ не рекомендуется. Кроме поля базового адреса можно изменить и поле разрядности сегмента, указав например, что сегмент данных 32 разрядный. Казалось бы, это именно то, что нужно. Но не спешите радоваться. Поведение процессоров разных моделей в этом случае будет различно. А ведь существуют еще и Intel совместимые процессоры, такие как AMD. Поэтому, если Вам нужна 32 разрядность, используйте операционную систему, работающую в Р режиме, например Windows 95/98/NT, или один из вариантов Unix.

Во вторых, появился режим виртуального 8086 (V режим). Теперь стало возможным запускать старые программы, написанные для 8086, и при этом операционная система сохраняла полный контроль над ситуацией.

В третьих, процессор получил возможность свободно переключаться между режимами, R, V и P. Операционные системы получили "свободу маневра".

В третьих, и пожалуй не менее важное, чем 32 разрядность, появился еще один уровень абстракции в преобразовании логических адресов в физические. Так называемая страничная адресация. Она позволила существенно упростить реализацию виртуальной памяти и добавила гибкости в управление памятью операционной системой. Вот эту страничную адресацию мы сейчас и рассмотрим.

Страничная трансляция адреса полностью прозрачна для прикладных программ. Она не отменяет и не заменяет сегментное преобразование адресов. Более того, страничная трансляция действует только в P и V режимах (трансляцию можно включить и в R режиме с помощью команды loadall, однако это не документированная команда). Операционная система может в любой момент включить и выключить страничную трансляцию.

Страничная трансляция действует после сегментного преобразования. Когда трансляция включена, адрес на выходе устройства сегментации называется ЛИНЕЙНЫМ АДРЕСОМ, физическим адресом в этом случае является адрес на выходе устройства трансляции страниц. Когда трансляция выключена, адрес с выхода устройства сегментации является физическим адресом. В страничном преобразовании адреса участвуют, кроме всех перечисленных ранее регистров, регистр CR3, каталог страниц и таблицы страниц.

Формат линейного адреса

Как видно из рисунка, линейный адрес состоит из трех частей. Смещение, как и смещение в сегментной адресации, определяет положение конкретного байта, только не в сегменте, а в странице. Таблица, определяет смещение от начала конкретной таблицы страниц, перед использованием этого поля процессор сдвигает его влево на 2 разряда, получая таким образом 12 битную величину. Каталог, определяет смещение от начала каталога страниц. Как и поле таблица, это поле перед использованием сдвигается влево на 2 разряда для получения 12 битного значения. Наличие двух полей для косвенной адресации (каталог и таблица) говорит о том, что страничное преобразование выполняется в два этапа. Так что можно сказать, что добавился не один уровень абстракции, а сразу два.

Формат каталога страниц

И каталог страниц, и таблицы страниц содержат записи одинакового формата. Разница только в том, что в каталоге страниц поле адрес указывает на таблицу страниц, а в таблице страниц на начало собственно страницы. Имеются различия и наборе полей описателя.

  • Поле Адрес. Физический адрес страницы или линейный адрес таблицы страниц. Точнее 20 старших бит адреса, поскольку выравнивание на границу страницы (адреса кратные 4К = 4096) подразумевает равенство нулю младших 12 разрядов адреса.

  • Поле OS. Это поле не используется процессором. Операционная система может использовать его для хранения своей информации, например, статистической информации.

  • Поле D. Используется в таблице страниц. Устанавливается процессором в 1, когда в страницу выполняется запись. Для каталога страниц не определен.

  • Поле A. Используется и в каталоге страниц и в таблице страниц. Устанавливается процессором в 1, когда проиходит обращение к странице или таблице страниц для чтения или записи.

  • Поле PCD. Запрещение кэширования страницы. Этот бит доступен только в процессорах начиная с 80486. В процессоре 80386 ДОЛЖЕН быть установлен в 0.

  • Поле PWT. Включение сквозной записи для кэшируемой страницы. Этот бит доступен только в процессорах начиная с 80486. В процессоре 80386 ДОЛЖЕН быть установлен в 0.

  • Поле U/S. Пользователь/супервизор. Если установлен, то к странице или таблице страниц разрешен доступ задач с уровнем защиты 3 (самый низкий уровень). Это поле, совместно с полем R/W, обеспечивает простейшую двухуровневую защиту памяти, дополнительно к защите, обеспечивоемой сегментным преобразованием.

  • Поле R/W. Запись/чтение. Определяет права доступа к странице для пользователя. Если бит U/S установлен, то нулевое значение бита R/W разрешает только читать страницу, а единичное еще и записывать.

  • Поле P. Бит присутствия. Если установлен, то страница присутствует в памяти. Если сброшен, то страница выгружена на диск, и остальные 31 разряд могут использоваться произвольным образом, например, для хранения начального номера сектора расположения страницы на диске.

Как видно, дескриптор страницы значительно проще дескрипотра сегмента. Не содержит он и информации для организации сложных систем защиты. Оно и понятно. Страничная адресация работает после сегментной, которая и обеспечивает необходимые уровни защиты. К которым, впрочем, добавляется еще один уровень, пусть и совсем простенький.

Формат регистра CR3

Местоположение каталога страниц в памяти определяется регистром CR3.

  • Физический адрес каталога страниц. Содержит старшие 20 разрядов физического адреса каталога в оперативной памяти. Так как каталог должен быть выровнен на границу страницы, младшие 12 разрядов всегда равны 0 и поэтому не хранятся.

  • PSD. Состояние этого бита выдается как сигнал процессора на линии PCD и показывает, является ли текущая страница кэшируемой. Если установлен, то кэширование страницы запрещено. Используется для управления внешней кэш памятью. В процессоре 80386 отсутствует и должен быть 0.

  • PWT. Состояние этого бита выдается как сигнал процессора на линии PWT. Если установлен, то запись в страницу производится немедленно. Если сброшен, то содержимое кэша переписывается в память по специальному запросу. В процессоре 80386 отсутствует и должен быть 0.

Управлять битами PCD и PWT Вам скорее всего не придется. Поэтому не расстраивайтесь сильно, если их назначение осталось для Вас туманным. Дело в том, что кроме кэша находящегося в процессоре, на материнских платах находится кэш второго уровня. Организация и тонкости работы этого кэша зависят от конкретного производителя. Вот для управления этим кэшем второго уровня и используются эти биты. Причем их нормальное состояние 0. Установка этих бит может решить проблемы при некорректной работе кэша на материнской плате. При этом естественно снизится и быстродействие.

Страничная адресация памяти

Еще раз напомню, что линейный адрес, это адрес после сегментного преобразования. Как видно из рисунка, страничная трансляция адреса выполняется в два этапа (этап сегментного преобразования не учитывается!). На первом этапе, через запись в каталоге страниц, извлекается адрес соответствующей таблицы страниц. На втором этапе, через запись в таблице страниц, извлекается адрес страничного кадра в физической памяти, к адресу страничного кадра прибавляется смещение, в результате получается адрес конкретного байта. Это уже физический адрес.

Как и для сегментного преобразования адреса, для страничной трансляции используются специальные решения для сокращения времени доступа. Это так называемый ассоциативный буфер преобразования TLB. Он удовлетворяет большинство запросов на считывание из таблиц страниц.

Несмотря на все преимущества страничной организации, безусловно, есть и недостатки. Причем они определяются самой архитектурой страничной адресации. Самый, пожалуй, очевидный недостаток - все страницы имеют одинаковую длину. Как же так? Ведь говорилось, что это как раз преимущество! Да, преимущество, для организации виртуальной памяти. Но представьте ситуацию, когда Вам требуется выделить всего пару десятков байт памяти. Система выделит целую страницу, 4096 байт, большая часть которой останется неиспользованной. Поэтому выбор конкретной схемы преобразования адресов является не такой простой задачей. Должно учитываться очень много параметров. И несмотря на это значительная часть принимаемых решений носит эмпирический (на основе опытных данных) характер.

Варианты построения систем управления памятью.

Два уровня абстракции. Два уровня организации защиты памяти. Все это позволяет организовать большое число вариантов управления памятью для операционной системы. Расмотрим несколько вариантов:

  • Страничная трансляция выключена, локальные дескрипторные таблицы не используются, дескрипторы в глобальной таблице указывают на начало физической памяти, кольца защиты не используются. В результате получается система, где все задачи и ядро ОС размещены в едином адресном пространстве, причем без какой бы то ни было защиты. Это некое подобие MS-DOS. С той разницей, что процессор работает в Р режиме и адресное пространство составляет 4Г байт. Про сегментные регистры можно вообще забыть. Это простейший вариант организации системы.

  • Страничная трансляция выключена, локальные дескрипторные таблицы не используются, дескрипторы в глобальной таблице указывают на начало физической памяти, используются 0 и 3 кольца защиты. Задачи и ядро ОС размещены в едином адресном пространстве. Однако ядро ОС уже защищено. Это классическая организация операционной системы с двумя уровнями привилегий: пользователь и супервизор.

  • Страничная трансляция выключена, каждая задача имеет свою локальную дескрипторную таблицу, внутренние модули ядра ОС имеют свою локальную дескрипторную таблицу, в глобальной таблице находятся описатели областей памяти и модулей ядра ОС доступные для прикладных программ, используются кольца защиты. Это уже солидная система. Обеспечивается необходимая защита ядра. Задачи изолированы друг от друга. Обеспечивается необходимый и защищенный сервис ОС для прикладных задач. Возможно создание общих областей памяти для взаимодействия задач. Если ОС будет поддерживать создание пседонимов для сегментов, будет возможно написание программ изменяющих свой код в процессе работы. Организация виртуальной памяти несколько затруднена различной длиной сегментов.

  • Страничная трансляция включена, каждая задача имеет свою локальную дескрипторную таблицу, внутренние модули ядра ОС имеют свою локальную дескрипторную таблицу, в глобальной таблице находятся описатели областей памяти и модулей ядра ОС доступные для прикладных программ, используются кольца защиты, используются режимы доступа пользователь/супервизор, каждая задача имеет свою таблицу страниц. Многозадачная и многопользовательская ОС с развитой системой управления и защиты памяти. Трудно сжато описать всю гибкость такой системы. Дополнительно к предыдущему варианту существенно улучшается управляемость виртуальных машин, вплоть за запуска гостевых ОС (MS-DOS под управлением OS/2, Windows 95 под управлением Linux). Гибкая система управления виртуальной памятью. Повышенные требования к быстродествию компьютера и объему доступной физической памяти. Повышенные накладные расходы.

Это лишь некоторые варианты. А ведь возможно и большое число промежуточных вариантов. Возможно изменение схем работы в зависимости от степени загрузки и характера выполняемых задач. Если еще учесть, что процессоры Intel 80х86 поддерживают концепцию задачи на аппаратном и архитектурном уровне, что кроме дескрипторов сегментов существуют дескрипторы задач и шлюзов, что кроме глобальной и локальной дескрипторных таблиц есть еще таблица прерываний (IDT), что можно очень гибко управлять доступными прикладным задачам портами ввода-вывода, что достаточно легко можно организовать эмуляцию отсутствующего оборудования (например можно эмулировать наличие стримера, а запись и чтение выполнять на диск или дискету), то становиться понятно, почему микропроцессоры 80х86 называют процессорами, и почему написание действительно полноценной операционной системы дело чрезвычайно сложное. И не только написание, но и понимание работы такой системы требует глубоких знаний.

Я сознательно не уделял внимания тонкостям управления памятью в процессорах Pentium, Pentium Pro, Celeron, AMD K6 и им подобным. Написание ОС для таких процессоров дело для больших коллективов разработчиков. А если Вам потребуется написать свою специализированную микро ОС для таких процессоров, то изложенного в этой статье более чем достаточно. Просто считайте, что все зарезервированные биты должны быть нулевыми.

Мы познакомились с очень сложной темой, адресацией физической памяти. Без этого знакомства невозможно понять поддержку задач, обработку прерываний, шлюзы, управление вводом-выводом и многое другое. Если Вы дочитали эту статью до конца, если Вам понравилось копаться в темных глубинах компьютеров и операционных систем, то ждите. Следующая статья будет о поддержке концепции задачи процессорами Intel 80х86.