Вопрос 34: Режимы процессора x86-32
Основным режимом работы микропроцессора является защищенный режим. Ключевыми особенностями защищенного режима являются: виртуальное адресное пространство, защита и многозадачность.
В защищенном режиме программа оперирует адресами, которые могут относиться к физически отсутствующим ячейкам памяти, поэтому такое адресное пространство называется виртуальным. Размер виртуального адресного пространства программы может превышать емкость физической памяти и достигать 64Тбайт. Для адресации виртуального адресного пространства используется сегментированная модель, в которой адрес состоит из двух элементов: селектора сегмента и смещения внутри сегмента. С каждым сегментом связана особая структура, хранящая информацию о нем, — дескриптор. Кроме «виртуализации» памяти на уровне сегментов существует возможность «виртуализации» памяти при помощи страниц — страничная трансляция. Страничная трансляция предоставляет удобные средства для реализации в операционной системе функций подкачки, а кроме того в процессорах P6+ обеспечивает 36-битную физическую адресацию памяти (64Гбайт).
Встроенные средства переключения задач обеспечивают многозадачность в защищенном режиме. Среда задачи состоит из содержимого регистров МП и всего кода с данными в пространстве памяти. Микропроцессор способен быстро переключаться из одной среды выполнения в другую, имитируя параллельную работу нескольких задач. Для некоторых задач может эмулироваться управление памятью как у процессора 8086. Такое состояние задачи называется режимом виртуального 8086 (Virtual 8086 Mode). О пребывании задачи в таком состоянии сигнализирует бит VM в регистре флагов. При этом задачи виртуального МП 8086 изолированы и защищены, как от друг друга, так и от обычных задач защищенного режима.
Защита задач обеспечивается следующими средствами: контроль пределов сегментов, контроль типов сегментов, контроль привилегий, привилегированные инструкции и защита на уровне страниц. Контроль пределов и типов сегментов обеспечивает целостность сегментов кода и данных. Программа не имеет права обращаться к виртуальной памяти, выходящей за предел того или иного сегмента. Программа не имеет права обращаться к сегменту данных как к коду и наоборот. Архитектура защиты микропроцессора обеспечивает 4 иерархических уровня привилегий, что позволяет ограничить задаче доступ к отдельным сегментам в зависимости от ее текущих привилегий. Кроме того, текущий уровень привилегий задачи влияет на возможность выполнения тех или иных специфических команд (привилегированных инструкций). Функции страничной трансляции, впервые появившиеся в МП Intel386, обеспечивают дополнительные механизмы защиты на уровне страниц.
Реальный режим (Real Mode)
В реальном режиме микропроцессор работает как очень быстрый 8086 с возможностью использования 32-битных расширений. Механизм адресации, размеры памяти и обработка прерываний (с их последовательными ограничениями) МП Intel386 в реальном режиме полностью совпадают с аналогичными функциями МП 8086. В отличие от 8086 микропроцессоры 286+ в определенных ситуациях генерируют исключения, например, при превышении предела сегмента, который для всех сегментов в реальном режиме — 0FFFFh.
Имеется две фиксированные области в памяти, которые резервируются в режиме реальной адресации:
- бласть инициализации системы
- область таблицы прерываний
Ячейки от 00000h до 003FFH резервируются для векторов прерываний. Каждое из 256 возможных прерываний имеет зарезервированный 4-байтовый адрес перехода. Ячейки от FFFFFFF0H до FFFFFFFFH резервируются для инициализации системы.
Режим системного управления (System Management Mode)
Режим системного управления предназначен для выполнения некоторых действий с возможностью их полной изоляции от прикладного программного обеспечения и даже операционной системы. Переход в этот режим возможен только аппаратно. Когда процессор находится в режиме SMM, он выставляет сигнал SMIACT#. Этот сигнал может служить для включения выделенной области физической памяти (System Management RAM), так что память SMRAM можно сделать доступной только для этого режима. При входе в режим SMM процессор сохраняет свой контекст в SMRAM (контекст сопроцессора не сохраняется) по адресу SMM Base и передает управление процедуре, называемой обработчиком System Management Interrupt, по адресу SMM Base+8000h (по умолчанию SMM Base содержит значение 30000h). Состояние процессора в этот момент точно определено: EFLAGS обнулен (кроме зарезервированных битов), сегментные регистры содержат селектор 0000, базы сегментов установлены в 00000000, пределы — 0FFFFFFFFh.
Следует отметить, что в режиме SMM не предусмотрена работа с прерываниями и особыми случаями: прерывания по IRQ и SMI# замаскированы, пошаговые ловушки и точки останова отключены, обработка прерывания по NMI откладывается до выхода из режима SMM. Если необходимо обеспечить работу с прерываниями или особыми случаями, то надо инициализировать IDT и разрешить прерывания, выставив флаг IF в регистре EFLAGS. Прерывания по NMI будут разблокированы автоматически после первой же команды IRET.
При возврате из SMM (по инструкции RSM) процессор восстанавливает свой контекст из SMRAM. Обработчик может программно внести изменения в образ контекста процессора, тогда процессор перейдет не в то состояние, в котором произошло SMI. Если SMI было получено во время выполнения инструкции HLT, то дальнейшие действия при выходе из SMM определяются значением поля «Auto HALT Restart»: процессор может снова вернуться к инструкции останова или перейти к выполнению следующей команды. Если SMI произошло при выполнении инструкции ввода-вывода, то в зависимости от значения поля «I/O Instruction Restart» возможен рестарт инструкции ввода вывода.
Эти особенности режима системного управления позволяют использовать его для реализации системы управления энергосбережением компьютера или функций безопасности и контроля доступа.
Режим системного управления изолирован от других режимов. Процессор переходит в этот режим только аппаратно: по низкому уровню на контакте SMI# или по команде с шины APIC (Pentium+). Никакой программный способ не предусмотрен для перехода в этот режим. Процессор возвращается из режима системного управления в тот режим, при работе в котором был получен сигнал SMI#. Возврат происходит по команде RSM. Эта команда работает только в режиме системного управления и в других режимах не распознается, генерируя исключение #6 (недействительный код операции).
Сравнение режимов
Адресация памяти в защищенном режиме работы процессора
Разделение адресного пространства на сегменты (сегментация)
В реальном режиме работы процессора адресное пространство процесса (программы) делится на равные части механически, без учета смыслового значения данных. В одном сегменте могут оказаться и коды команд, и инициализируемые переменные, и массив исходных данных программы. Такой подход не позволяет обеспечить дифференцированный доступ к разным частям программы, а это свойство могло бы быть очень полезным во многих случаях. Например, можно было бы запретить обращаться с операциями записи в сегмент программы, содержащий коды команд, разрешив эту операцию для сегментов данных. Также полезно запретить операции записи со стороны прикладных процессов для системных таблиц, очевидно, что, сама операционная система при этом должна иметь возможность менять содержимое этих таблиц. Кроме того, разбиение виртуального адресного пространства на «осмысленные» части (которые и называются сегментами) делает принципиально возможным совместное использование фрагментов программ разными процессами. Пусть, например, двум процессам требуется одна и та же подпрограмма, тогда в память достаточно загрузить только одну копию кода. Коды этой подпрограммы могут быть оформлены в виде отдельного сегмента и включены в виртуальные адресные пространства обоих процессов. Таким образом, оба процесса получат доступ к одной и той же копии подпрограммы. Итак, адресное пространство процесса делится на части — сегменты, размер которых определяется с учетом смыслового значения содержащейся в них информации. Отдельный сегмент может представлять собой подпрограмму, массив данных и т. п. (Еще раз прошу обратить внимание – в реальном режиме размеры всех сегментов одинаковые (64 Кбайта), а в защищенном каждый сегмент имеет собственный размер.) На этапе создания процесса во время загрузки его образа в оперативную память система создает в памяти локальную таблицу дескрипторов–описателей сегментов (LDT) процесса. Каждый дескриптор описывает свой сегмент. Кроме локальных таблиц дескрипторов сегментов система создает одну глобальную таблицу дескрипторов (GDT), где хранятся дескрипторы сегментов, занятых операционной системой. Доступ к глобальной таблице имеют все выполняемые задачи. Независимо от вида таблицы каждый дескриптор содержит следующую информацию, характеризующую сегмент:
- базовый адрес сегмента в оперативной памяти. Сегмент может начинаться в любой точке адресного пространства объемом 2 32 = 4Гбайт. Это адресное пространство носит название линейного, а адрес в нем соответственно линейным адресом (для процессоров с разрядностью адресной шины 36 разрядов объем адресного пространства будет соответственно 2 36 = 64Гбайт.);
- размер сегмента в байтах. Максимальный размер сегмента определяется разрядностью линейного адреса, при 32-разрядной организации процессора он равен 4 Гбайт;
- тип сегмента (сегмент кода, сегмент данных, системный сегмент) и права на его использование (возможность модифицировать сегмент кода, считывать содержимое сегмента или выполнять этот код);
- право доступа (позволяет указать, что данный сегмент доступен только для чтения);
- уровень привилегий сегмента.
Рассмотрим понятие уровень привилегий более детально. Привилегии это свойство, которое определяет, какие компьютерные операции разрешаются в любой момент времени и какие доступы к памяти законны. Привилегии используются для обеспечения безопасности в компьютерной системе. Защита на уровне сегментов представлена четырьмя уровнями привилегий (Privilege Level, PL). Четыре уровня привилегий можно интерпретировать в виде колец защиты (рис. 22). Центр — уровень 0 — предназначен для сегментов, содержащих наиболее критичные программы (обычно ядро операционной системы). Внешние кольца предназначены для сегментов с менее критичными программами или данными.Обычно процессы имеют следующие привилегии:
- 0 – ядро операционной системы, системные драйверы.
- 1 – программы обслуживания аппаратуры, драйверы, программы, работающие с портами ввода/вывода компьютера.
- 2 – системы управления базами данных, расширения операционной системы.
- 3 – прикладные программы, запускаемые пользователем.
Рисунок 22 Кольца защиты системы Программа может обратиться к сегменту данных, который находится на том же или более низком уровне привилегий т.е. программе будет предоставлен доступ к сегменту только в том случае, когда уровень привилегий дескриптора запрашиваемого сегмента больше или равен ее уровню привилегий. Если доступ к данным не разрешен, система генерирует прерывание «нарушение защиты», в этом случае вы увидите примерно такое сообщение (рис. 23). Рисунок 23 Сообщение системы в случае нарушения защиты Теперь разберемся, как формируется нужный нам физический адрес. Преобразование виртуального адреса в физический происходит в два этапа. На первом этапе работает механизм сегментации, который мы и разбираем. Так же как и в случае реального режима будет использоваться двухкомпонентный логический адрес. По-прежнему логический адрес формируется при помощи сегментных регистров и регистров, где хранится смещение. Однако сегментные регистры содержат теперь не сегментный адрес, а так называемый селектор, часть которого (13 бит) представляет собой индекс в дескрипторной таблице, т.е. указывает на дескриптор, в котором находится полная информация о сегменте. Как это происходит показано на рис. 24. .Иначе это преобразование называется трансляция логического адреса в линейный. Логический адрес состоит из 16-битового селектора сегмента (записываемого в специальный сегментный регистр) и 32-битового смещения в этом сегменте. Логический адрес преобразуется в линейный адрес сложением смещения и адреса сегмента. Адрес сегмента берется из таблицы глобальных дескрипторов (GDT), либо из таблицы локальных дескрипторов (LDT). Бит Т (13-й разряд) в селекторе сегмента определяет, какая таблица (глобальная или локальная) должна быть использована, а собственно сам дескриптор определяется на основании 13 младших разрядов селектора сегмента. Можно сказать, что эти младших 13 разрядов являются 13-разрядным индексом в таблице дескрипторов, а еще проще, номером нужного нам дескриптора в таблице. На основании известного системе адреса таблицы дескрипторов (определяется специальными регистрами) и номера сегмента вычисляется адрес дескриптора сегмента. Получив доступ к дескриптору, анализируются его поля, выполняется проверка возможности выполнения заданной операции (возможность доступа к сегменту). Если доступ разрешен, то из дескриптора извлекается адрес этого сегмента и вычисляется линейный адрес. Если доступ невозможен, возникает прерывание. Для нахождения линейного адреса к адресу сегмента добавляется смещение, заданное в исходном логическом адресе. Как мы уже писали, смещение хранится в специальном 32- разрядном регистре. С точки зрения процесса смещение это и есть адрес внутри сегмента, он находятся в диапазоне от 00000000h до FFFFFFFFh.. В результате проведенного преобразования мы получили требуемый 32- разрядный линейный адрес. Полученный линейный адрес является виртуальным, т.е. не может быть непосредственно отправлен на шину адреса. Для перехода от линейного адреса к физическому требуется второй этап преобразования.
Защищенный режим — Protected mode
В вычислениях защищенный режим, также называемый режимом защищенного виртуального адреса, является режим работы x86 -совместимых центральных процессоров (CPU). Это позволяет системному программному обеспечению использовать такие функции, как виртуальная память, разбиение на страницы и безопасная многозадачность, предназначенные для увеличения контроля операционной системы над прикладное программное обеспечение.
Когда процессор, поддерживающий защищенный режим x86, включен, он начинает выполнять инструкции в реальном режиме, чтобы поддерживать обратную совместимость с более ранними процессорами x86.. В защищенный режим можно войти только после того, как системное программное обеспечение установит одну таблицу дескрипторов и активирует бит включения защиты (PE) в регистре управления 0 (CR0).
Защищенный режим был впервые добавлен в архитектуру x86 в 1982 году с выпуском процессора Intel 80286 (286), а затем расширен с выпуском из 80386 (386) в 1985 году. Благодаря усовершенствованиям, добавленным в защищенный режим, он получил широкое распространение и стал основой для всех последующих улучшений архитектуры x86, хотя многие из этих улучшений, такие как как добавленные инструкции и новые регистры, также принесла пользу в реальном режиме.
- 1 История
- 1.1 286
- 1.2 386
- 4.1 Уровни привилегий
- 4.2 Совместимость приложений в реальном режиме
- 4.3 Виртуальный режим 8086
- 4.4 Адресация сегмента
- 4.4.1 Защищенный режим
- 4.4.2 286
- 4.4.3 386
- 4.4.4 Структура записи дескриптора сегмента
История
The Intel 8086, предшественник 286, изначально был разработан с 20- -битной адресной шиной для своей памяти. Это позволило процессору получить доступ к 2 байтам памяти, что эквивалентно 1 мегабайту. В то время 1 мегабайт считался относительно большим объемом памяти, поэтому разработчики IBM Personal Computer зарезервировали первые 640 килобайт для использования приложениями и операционной системой и оставшиеся 384 килобайта для BIOS (базовая система ввода / вывода) и память для дополнительных устройств.
По мере уменьшения стоимости памяти и увеличения использования памяти Ограничение 1 МБ стало серьезной проблемой. Intel намеревалась устранить это ограничение вместе с другими с выпуском 286.
286
Первоначальный защищенный режим, выпущенный с 286, широко не использовался ; например, он использовался Microsoft Xenix (около 1984 г.), Coherent и Minix. Несколько недостатков, таких как невозможность доступа к BIOS или вызовам DOS из-за невозможности вернуться в реальный режим без перезагрузки процессора, препятствовали широкому использованию. Принятие было дополнительно затруднено тем фактом, что 286 разрешал доступ к памяти только в 16-битных сегментах через каждый из четырех сегментных регистров, то есть только 4 * 2 байта, эквивалентные 256 килобайтам, могли быть доступны одновременно. Поскольку изменение регистра сегмента в защищенном режиме привело к загрузке 6-байтового дескриптора сегмента в ЦП из памяти, инструкция загрузки регистра сегмента заняла много десятков циклов процессора, что сделало ее намного медленнее, чем на 8086; поэтому стратегия вычисления сегментных адресов на лету для доступа к структурам данных размером более 128 килобайт (объединенный размер двух сегментов данных) стала непрактичной даже для тех немногих программистов, которые освоили это на 8086/8088.
286 поддерживал обратную совместимость со своим предшественником 8086, изначально переходя в реальный режим при включении питания. Реальный режим функционировал практически так же, как и 8086, позволяя подавляющему большинству существующего программного обеспечения 8086 работать без изменений на новом 286. Реальный режим также служил более базовым режимом, в котором можно было установить защищенный режим, решая проблему. своего рода проблема с курицей и яйцом. Чтобы получить доступ к расширенным функциям 286, операционная система создала бы некоторые таблицы в памяти, которые контролировали доступ к памяти в защищенном режиме, установили адреса этих таблиц в некоторые специальные регистры процессора, а затем установили бы процессор в защищенный режим. Это обеспечило 24-битную адресацию, которая позволила процессору получить доступ к 2 байтам памяти, что эквивалентно 16 мегабайтам.
386
Микропроцессор Intel 80386
С выпуском 386 в 1985 году многие из решены проблемы, препятствующие широкому распространению предыдущего защищенного режима. Модель 386 была выпущена с размером адресной шины 32 бита, что позволяет использовать 2 байта памяти, что эквивалентно 4 гигабайтам. Размеры сегментов также были увеличены до 32 бит, что означает, что можно было получить доступ ко всему адресному пространству в 4 гигабайта без необходимости переключения между несколькими сегментами. В дополнение к увеличенному размеру адресной шины и регистров сегментов было добавлено много других новых функций с целью повышения операционной безопасности и стабильности. Защищенный режим сейчас используется практически во всех современных операционных системах, работающих на архитектуре x86, таких как Microsoft Windows, Linux и многих других.
Кроме того, изучая отказы защищенного режима 286 для удовлетворения потребностей многопользовательской DOS, Intel добавила отдельный виртуальный режим 8086, который позволил несколько виртуализированных 8086 процессоров для эмуляции на 386. Однако аппаратная поддержка, необходимая для виртуализации самого защищенного режима, должна была подождать еще 20 лет.
386 дополнений к защищенному режиму
С В версии 386 в защищенный режим были добавлены следующие дополнительные функции:
- Пейджинг
- 32-битное физическое и виртуальное адресное пространство (32-битное физическое адресное пространство отсутствует на 80386SX и других 386 вариантах процессоров, использующих старую шину 286.)
- 32-битный сегмент смещения
- Возможность обратного переключения в реальный режим без сброс
- виртуального режима 8086
Вход и выход из защищенного режима
До выпуска модели 386 защищенный режим не предлагал прямого метода переключения обратно в реальный режим после входа в защищенный режим. IBM разработала обходной путь (реализованный в IBM AT ), который включал сброс ЦП с помощью контроллера клавиатуры и сохранение системных регистров, указателя стека и часто прерывания маска в ОЗУ микросхемы часов реального времени. Это позволило BIOS восстановить процессор до аналогичного состояния и начать выполнение кода до сброса. Позже для сброса ЦП 286 использовалась тройная ошибка, которая была намного быстрее и чище, чем метод контроллера клавиатуры (и не зависит от IBM AT-совместимого оборудования, но будет работать на любом ЦП 80286. в любой системе).
Чтобы войти в защищенный режим, необходимо сначала создать глобальную таблицу дескрипторов (GDT) как минимум с тремя записями: нулевой дескриптор, дескриптор сегмента кода и дескриптор сегмента данных. На IBM-совместимом компьютере строка A20 (21-я адресная строка) также должна быть включена, чтобы разрешить использование всех адресных строк, чтобы ЦП мог получить доступ к памяти, превышающей 1 мегабайт (только первые 20 разрешено использовать после включения питания, чтобы гарантировать совместимость со старым программным обеспечением, написанным для моделей IBM PC и PC / XT на базе Intel 8088). После выполнения этих двух шагов в регистре CR0 должен быть установлен бит PE и должен быть сделан дальний переход, чтобы очистить входную очередь предварительной выборки .
; установить бит PE mov eax, cr0 или eax, 1 mov cr0, eax; дальний прыжок (cs = селектор сегмента кода) jmp cs: @pm @pm:; Сейчас мы в личку.
С выпуском 386 из защищенного режима можно было выйти, загрузив в сегментные регистры значения реального режима, отключив линию A20 и сбросив бит PE в регистре CR0, без необходимости выполнять начальные шаги настройки, необходимые с the 286.
Функции
Защищенный режим имеет ряд функций, разработанных для улучшения контроля операционной системы над прикладным программным обеспечением, чтобы повысить безопасность и стабильность системы. Эти дополнения позволяют операционной системе работать так, как это было бы значительно сложнее или даже невозможно без надлежащей аппаратной поддержки.
Уровни привилегий
Пример использования кольца привилегий в операционной системе, использующей все кольца
В защищенном режиме существует четыре уровня привилегий или кольца, пронумерованные от 0 до 3, причем кольцо 0 является наиболее привилегированным, а 3 — наименьшим. Использование колец позволяет системному программному обеспечению ограничивать доступ задач к данным, шлюзы вызова или выполнение привилегированных инструкций. В большинстве сред операционная система и некоторые драйверы устройств работают в кольце 0, а приложения — в кольце 3.
Совместимость приложений в реальном режиме
Согласно данным программиста Intel 80286 Справочное руководство,
. 80286 остается совместимым снизу вверх с большинством прикладных программ 8086 и 80186. Большинство прикладных программ 8086 можно перекомпилировать или перекомпилировать и запустить на 80286 в защищенном режиме.
По большей части двоичная совместимость с кодом реального режима, возможность доступа к физической памяти до 16 МБ, и 1 ГБ виртуальной памяти, были наиболее очевидными изменениями для прикладных программистов. Это было не без ограничений. Если бы приложение использовало или полагалось на какой-либо из приведенных ниже методов, оно не могло бы работать:
- Сегментная арифметика
- Привилегированные инструкции
- Прямой доступ к оборудованию
- Запись в сегмент кода
- Выполнение данных
- Перекрывающиеся сегменты
- Использование функций BIOS из-за того, что прерывания BIOS зарезервированы Intel
На самом деле, почти все DOS прикладные программы нарушили эти правила. Из-за этих ограничений виртуальный режим 8086 был введен в 386. Несмотря на такие потенциальные неудачи, Windows 3.0 и ее преемники могут использовать преимущества двоичной совместимости с реальным режимом для запуска многих Windows. 2.x (Windows 2.0 и Windows 2.1x ), которые работают в реальном режиме в Windows 2.x, в защищенном режиме.
Виртуальный режим 8086
С выпуском 386 защищенный режим предлагает то, что в руководствах Intel называют виртуальным режимом 8086. Виртуальный режим 8086 предназначен для того, чтобы код, ранее написанный для 8086, мог выполняться без изменений и одновременно с другими задачами без ущерба для безопасности или стабильности системы.
Виртуальный режим 8086, однако, не полностью обратно совместим со всеми программами. Программы, которые требуют манипулирования сегментами, привилегированных инструкций, прямого доступа к оборудованию или используют самомодифицирующийся код, будут генерировать исключение, которое должно обслуживаться операционной системой. Кроме того, приложения, работающие в виртуальном режиме 8086, генерируют ловушку с использованием инструкций, которые включают ввод / вывод (I / O), что может отрицательно сказаться на производительности.
Из-за этих ограничений некоторые программы, изначально разработанные для работы на 8086, не могут быть запущены в виртуальном режиме 8086. В результате системное программное обеспечение вынуждено либо ставить под угрозу безопасность системы, либо обеспечивать обратную совместимость при работе с устаревшим программным обеспечением. Пример такого компромисса можно увидеть в выпуске Windows NT, в котором упала обратная совместимость для «плохо себя ведающих» приложений DOS.
Адресация сегментов
виртуальных сегментов 80286
В реальном режиме каждый логический адрес указывает непосредственно на ячейку физической памяти, каждый логический адрес состоит из двух частей по 16 бит: Сегментная часть логического адреса содержит базовый адрес сегмента с гранулярностью 16 байтов, т. Е. Сегмент может начинаться с физического адреса 0, 16, 32. 2-16. Часть смещения логического адреса содержит смещение внутри сегмента, то есть физический адрес может быть вычислен как физический_адрес: = сегмент_часть × 16 + смещение (если адрес строка A20 активирована) соответственно (segment_part × 16 + offset) mod 2 (если A20 выключен) Каждый сегмент имеет размер 2 байта.
Защищенный режим
В защищенном режиме сегмент_часть заменяется 16-битным селектором, в котором 13 старших битов (бит 3 — бит 15) содержат индекс записи внутри таблица дескрипторов. Следующий бит (бит 2) указывает, используется ли операция с GDT или LDT. Два младших бита (бит 1 и бит 0) селектора объединяются для определения привилегии запроса, где значения 0 и 3 представляют наивысшую и самую низкую привилегию, соответственно. Это означает, что байтовое смещение дескрипторов в таблице дескрипторов такое же, как у 16-битного селектора, при условии, что младшие три бита обнулены.
Запись таблицы дескрипторов определяет реальный линейный адрес сегмента, предельное значение для размера сегмента и некоторые биты атрибутов (флаги).
286
Адрес сегмента внутри записи таблицы дескрипторов имеет длину 24 бита, поэтому каждый байт физической памяти может быть определен как граница сегмента. Предельное значение внутри записи таблицы дескрипторов имеет длину 16 бит, поэтому длина сегмента может составлять от 1 до 2 байтов. Вычисленный линейный адрес равен адресу физической памяти.
386
Адрес сегмента внутри записи таблицы дескрипторов расширяется до 32 бит, поэтому каждый байт физической памяти может быть определен как граница сегмента. Предельное значение внутри записи таблицы дескрипторов расширяется до 20 бит и дополняется флагом гранулярности (G-бит, для краткости):
- Если G-бит равен нулю, предел имеет гранулярность 1 байт, т.е. размер сегмента может быть 1, 2. 2 байта.
- Если G-бит равен одному пределу, имеет гранулярность 2 байта, то есть размер сегмента может быть 1 × 2, 2 × 2. 2 × 2 байта. Если подкачка отключена, вычисленный линейный адрес равен адресу физической памяти. Если пейджинг включен, вычисленный линейный адрес используется как вход пейджинга.
Процессор 386 также использует 32-битные значения для смещения адреса.
Для обеспечения совместимости с 286 защищенным режимом был добавлен новый флаг по умолчанию (для краткости D-бит). Если бит D сегмента кода выключен (0), все команды внутри этого сегмента по умолчанию будут интерпретироваться как 16-битные команды; если он включен (1), они будут интерпретироваться как 32-битные команды.
Структура записи дескриптора сегмента
- A — бит доступа;
- R — бит чтения;
- C (бит 42) зависит от X:
- если X = 1, то C является битом согласования и определяет, какие уровни привилегий могут перейти на этот сегмент (без изменения уровня привилегий):
- если C = 0, то сюда может перейти только код с тем же уровнем привилегий, что и DPL;
- если C = 1, то сюда может перейти код с таким же или более низким уровнем привилегий относительно DPL.
- если C = 0, то сегмент растет;
- если C = 1, то сегмент растет вниз.
- , если X = 1, тогда сегмент является сегментом кода;
- если X = 0, тогда сегмент является сегментом данных.
Пейджинг
Общий метод использования пейджинга для создать виртуальное адресное пространство Пейджинг (на Intel 80386) с размером страницы 4K
В дополнение к добавлению виртуального режима 8086, 386 также добавил пейджинг в защищенный режим. Посредством разбиения на страницы системное программное обеспечение может ограничивать и контролировать доступ задачи к страницам, которые представляют собой разделы памяти. Во многих операционных системах подкачка используется для создания независимого виртуального адресного пространства для каждой задачи, предотвращая манипулирование одной задачей с памятью другой. Пейджинг также позволяет перемещать страницы из первичной памяти в более медленную и большую вторичную память, такую как жесткий диск. Это позволяет использовать больше памяти, чем физически доступно в первичном хранилище.
Архитектура x86 позволяет управлять страницами через два массива : каталоги страниц и таблицы страниц. Первоначально каталог страниц имел размер одной страницы, четыре килобайта, и содержал 1024 записи каталога страниц (PDE), хотя последующие усовершенствования архитектуры x86 добавили возможность использовать страницы большего размера. Каждый PDE содержал указатель на таблицу страниц. Таблица страниц также изначально имела размер четыре килобайта и содержала 1024 записи таблицы страниц (PTE). Каждый PTE содержит указатель на фактический физический адрес страницы и используется только при использовании четырехкибайтных страниц. В любой момент времени может использоваться только один каталог страниц.
Многозадачность
Благодаря использованию звонков, привилегированных шлюзов вызова и Сегмент состояния задачи (TSS), представленный в 286, вытесняющая многозадачность стала возможной в архитектуре x86. TSS позволяет изменять регистры общего назначения, поля селектора сегментов и стеки, не затрагивая регистры другой задачи. TSS также позволяет уровню привилегий задачи и разрешениям порта ввода-вывода быть независимыми от другой задачи.
Во многих операционных системах не используются все функции TSS. Обычно это происходит из-за проблем с переносимостью или из-за проблем с производительностью, создаваемых аппаратными переключателями задач. В результате многие операционные системы используют как оборудование, так и программное обеспечение для создания многозадачной системы.
Операционные системы
Операционные системы, такие как OS / 2 1.x, пытаются переключиться процессор между защищенным и реальным режимами. Это и медленно, и небезопасно, потому что программа в реальном режиме может легко вывести компьютер из строя. OS / 2 1.x определяет правила ограничительного программирования, позволяющие API семейства или связанной программе работать в реальном или защищенном режиме. Некоторые ранние операционные системы Unix, OS / 2 1.x и Windows использовали этот режим.
Windows 3.0 могла запускать программы в реальном режиме в 16-битном защищенном режиме; при переключении в защищенный режим было решено сохранить модель с одним уровнем привилегий, которая использовалась в реальном режиме, поэтому приложения Windows и библиотеки DLL могут перехватывать прерывания и осуществлять прямой доступ к оборудованию. Так продолжалось в серии Windows 9x. Если программа Windows 1.x или 2.x написана правильно и избегает сегментной арифметики, она будет работать одинаково как в реальном, так и в защищенном режиме. Программы Windows обычно избегают сегментной арифметики, потому что Windows реализует схему виртуальной памяти программного обеспечения, перемещая программный код и данные в памяти, когда программы не запущены, поэтому манипулирование абсолютными адресами опасно; программы должны сохранять дескрипторы только для блоков памяти, когда они не работают. Запуск старой программы, когда Windows 3.0 работает в защищенном режиме, вызывает диалоговое окно с предупреждением, предлагающее либо запустить Windows в реальном режиме, либо получить обновленную версию приложения. При обновлении программ с хорошим поведением с помощью утилиты MARK с параметром MEMORY этот диалог не появляется. Невозможно запустить некоторые программы с графическим интерфейсом в 16-битном защищенном режиме, а другие программы с графическим интерфейсом — в реальном режиме. В Windows 3.1 реальный режим больше не поддерживался, и к нему нельзя было получить доступ.
В современных 32-битных операционных системах виртуальный режим 8086 по-прежнему используется для запуска приложений, например DPMI совместимые программы DOS extender (через виртуальные машины DOS ) или приложения Windows 3.x (через подсистему Windows в Windows ) и определенные классы драйверов устройств (например, для изменения разрешения экрана с помощью функций BIOS) в OS / 2 2.0 (и более поздних версиях OS / 2) и 32-битных Windows NT, все под управлением 32-битного ядра. Однако 64-битные операционные системы (которые работают в длинном режиме ) больше не используют это, поскольку виртуальный режим 8086 был удален из длинного режима.
См. Также
- Длинный режим
- Язык ассемблера
- Intel
- Кольцо (компьютерная безопасность)
- язык ассемблера x86
Ссылки
Внешние ссылки
- Основы защищенного режима
- Введение в защищенный режим
- Обзор операций в защищенном режиме архитектуры Intel
- Учебник TurboIRC.COM для входа в защищенный режим из DOS
- Обзор защищенного режима и учебное пособие
- Код Учебное пособие по защищенному режиму проекта
- Переключение Akernelloader из реального режима в защищенный
Основы защищенного режима
Микропроцессоры Pentium, так же, как и его предшественники (начиная с 80268), могут работать в двух режимах: реального адреса и виртуального защищенного адреса. Обычно эти режимы называют просто реальным и защищенным. В реальном режиме 32-разрядные микропроцессоры функционируют фактически так же, как МП 86 с повышенным быстродействием и расширенным набором команд. Многие весьма привлекательные возможности микропроцессоров принципиально не реализуются в реальном режиме, который введен лишь для обеспечения совместимости с предыдущими моделями процессоров. Характерной особенностью реального режима является ограничение объема адресуемой оперативной памяти величиной 1 Мбайт.
Только перевод микропроцессора в защищенный режим позволяет полностью реализовать все возможности, загаженные в его архитектуру и недоступные в реальном режиме. Сюда можно отнести:
— увеличение адресуемого пространства до 4 Гбайт;
— возможность работать в виртуальном адресном пространстве, превышающем максимально возможный объем физической памяти и составляющем огромную величину 64 Тбайт;
— организация многозадачного режима с параллельным выполнением нескольких программ (процессов). Собственно говоря, многозадачный режим организует многозадачная операционная система, однако микропроцессор предоставляет необходимый для этого режима мощный и надежный механизм защиты задач друг от друга с помощью четырехуровневой системы привилегий;
— страничная организация памяти, повышающая уровень защиты задач
друг от друга и эффективность их выполнения.
При включении микропроцессора в нем автоматически устанавливается режим реального адреса. Переход в защищенный режим осуществляется программно путем выполнения соответствующей последовательности команд. Поскольку многие детали функционирования микропроцессора в реальном и защищенном режимах существенно различаются, программы, предназначенные для защищенного режима, должны быть написаны особым образом. При этом различия реального и защищенного режимов настолько велики, что программы реального режима не могут выполняться в защищенном режиме и наоборот. Другими словами, реальный и защищенный режимы не совместимы.
Архитектура современного микропроцессора необычайно сложна. Столь же сложными оказываются и средства защищенного режима, а также программы, использующие эти средства. С другой стороны, при решении многих прикладных задач (например, при отладке приложений Windows, работающих, естественно, в защищенном режиме), нет необходимости в понимании всех деталей функционирования защищенного режима; достаточно иметь знакомство с его основными понятиями. В настоящем разделе дается краткое описание основ защищенного режима и его наиболее важных структур и алгоритмов функционирования.
Пожалуй, наиболее важным отличием защищенного режима от реального является иной принцип формирования физического адреса. Вспомним, что в реальном режиме физический адрес адресуемой ячейки памяти состоит из двух компонентов — сегментного адреса и смещения. Оба компонента имеют размер 16 бит, и процессор, обращаясь к памяти, пользуется следующим правилом вычисления физического адреса:Физический адрес = сегментный адрес * 16 + смещение
И сегментный адрес, и смещение не могут быть больше FFFFh, откуда следуют два важнейших ограничения реального режима: объем адресного пространства составляет всего 1 Мбайт, а сегменты не могут иметь размер, превышающий 64 Кбайт.
В защищенном режиме программа по-прежнему состоит из сегментов, адресуемых с помощью 16-разрядных сегментных регистров, однако местоположение сегментов в физической памяти определяется другим способом.
В сегментные регистры в защищенном режиме записываются не сегментные адреса, а так называемые селекторы, биты 3. 15 которых рассматриваются, как номера (индексы) ячеек специальной таблицы, содержащей дескрипторы сегментов программы. Таблица дескрипторов обычно создастся операционной системой защищенного режима (например, системой Windows) и, как правило, недоступна программе. Каждый дескриптор таблицы дескрипторов имеет размер 8 байт, и в нем хранятся все характеристики, необходимые процессору для обслуживания этого сегмента. Среди этих характеристик необходимо выделить в первую очередь две: адрес сегмента и его длину (рис. 4.4).Рис. 4.4. Дескрипторы сегментов и их селекторы.
Под адрес сегмента в дескрипторе выделяется 32 бит, и, таким образом, сегмент может начинаться в любой точке адресного пространства объемом 23- = 4 Гбайт. Это адресное пространство носит название линейного. В простейшем случае, когда выключено страничное преобразование, о котором речь будет идти позже, линейные адреса отвечают физическим. Таким образом, процессор может работать с оперативной памятью объемом до 4 Гбайт.
Как и в реальном режиме, адрес адресуемой ячейки вычисляется процессором, как сумма базового адреса сегмента и смещения:
Линейный адрес = базовый адрес сегмента + смещение
В 32-разрядных процессорах смещение имеет размер 32 бит, поэтому максимальная длина сегмента составляет 2″ = 4 Гбайт.
На рис. 4.4 приведен гипотетический пример программы, состоящей из трех сегментов, первый из которых имеет длину 1 Мбайт и расположен в начале адресного пространства, второй, размером 100 Кбайт, вплотную примыкает к первому, а третий, имеющий размер всего 256 байт, расположен в середине девятого по счету мегабайта.
Адреса, используемые программой для обращения к ячейкам памяти, и состоящие всегда из двух компонентов — селектора и смещения — иногда называются виртуальными. Система сегментной адресации преобразует виртуальные адреса в линейные. Поскольку таблица дескрипторов, с помощью которой осуществляется это преобразование, обычно недоступна программе, программа может не знать, в каких именно участках логического адресного пространства находятся се компоненты. Фактически это сводится к тому, что, загружая программу в память, вы не знаете, в каких местах памяти будут находиться ее сегменты, и каков будет порядок их размещения. Программисту доступны только виртуальные адреса, преобразование же их в линейные и затем в физические берет на себя операционная система.
Каков объем виртуального адресного пространства? Программа указывает номер нужного ей дескриптора с помощью селектора, в котором для индекса дескриптора отведено 13 бит. Отсюда следует, что в дескрипторной таблице может быть до 1″ = 8. К дескрипторов. Однако в действительности их в два раза больше, так как программа может работать не с одной, а с двумя дескрипторными таблицами — одной глобальной, разделяемой всеми выполняемыми задачами, и одной локальной, принадлежащей конкретной задаче. В селекторе предусмотрен специальный бит (бит 2), состояние которого говорит о типе требуемой программе дескрипторной таблицы. Таким образом, всего программе могут быть доступны 214 = 16 К дескрипторов, т.е. 16 К сегментов. Поскольку размер каждого сегмента, определяемый максимальной величиной смещения, может достигать 2-1 = 4 Гбайт, объем виртуального адресного пространства оказывается равным 16 К * 4 Кбайт = = 64 Тбайт.
Реально, однако, оперативная память компьютера с 32-разрядной адресной шиной не может быть больше 4 Гбайт, т.е. при сделанных выше предположениях (16 К сегментов размером 4 Гбайт каждый) в памяти может поместиться максимум один сегмент из более чем 16 тысяч. Где же будут находиться все остальные?
Полный объем виртуального пространства может быть реализован только с помощью многозадачной операционной системы, которая хранит все неиспользуемые в настоящий момент сегменты на диске, загружая их в память по мере необходимости. Разумеется, если мы хотим полностью реализовать возможности, заложенные в современные процессоры, нам потребуется диск довольно большого объема — 64 Тбайт. Однако и при нынешних более скромных технических средствах (память до 100 Мбайт, жесткий диск до 10 Гбайт) принцип виртуальной памяти используется всеми многозадачными операционными системами с большой эффективностью. С другой стороны, для прикладного программиста этот вопрос не представляет особого интереса, так как сброс сегментов на диск и подкачка их с диска осуществляются операционной системой, а не программой, и вмешательство эту процедуру вряд ли целесообразно.
Как уже отмечалось, адрес, вычисляемый процессором на основе селектора и смещения, относится к линейному адресному пространству, не обязательно совпадающему с физическим. Преобразование линейных адресов в физические осуществляется с помощью так называемой страничной трансляции, частично реализуемой процессором, а частично — операционной системой. Если страничная трансляция выключена, все ли-нейные адреса в точности совпадают с физическими; если страничная трансляция включена, то линейные адреса преобразуются в физические в соответствии с содержимым страничных таблиц (рис. 4.5).Рис. 4.5. Цепочка преобразований виртуального адреса в физический.
Страницей называется связный участок линейного или физического адресного пространства объемом 4 Кбайт. Программа работает в линейном адресном пространстве, не подозревая о существовании страничного преобразования или даже самих страниц. Механизм страничной трансляции отображает логические страницы на физические в соответствии с информацией, содержащейся в страничных таблицах. В результате отдельные 4х-килобайтовыс участки программы могут реально находиться в любых несвязных друг с другом 4х-килобайтовых областях физической памяти (рис. 4.6). Порядок размещения физических страниц в памяти может не соответствовать (и обычно не соответствует) порядку следования логических страниц. Более того, некоторые логические страницы могут перекрываться, фактически сосуществуя в одной и той же области физической памяти.
Страничная трансляция представляет собой довольно сложный механизм, в котором принимают участие аппаратные средства процессора и находящиеся в памяти таблицы преобразования. Назначение и взаимодействие элементов системы страничной трансляции схематически изображено на рис. 4.7.
Система страничных таблиц состоит из двух уровней. На первом уровне находится каталог таблиц страниц (или просто каталог страниц) — резидентная в памяти таблица, содержащая 1024 4х-байтовых поля с адресами таблиц страниц. На втором уровне находятся таблицы страниц, каждая из которых содержит так же 1024 4х-байтовых поля с адресами физических страниц памяти. Максимально возможное число таблиц страниц определяется числом полей в каталоге и может доходить до 1024. Поскольку размер страницы составляет 4 Кбайт, 1024 таблицы по 1024 страницы перекрывают все адресное пространство (4 Гбайт).Рис. 4.6. Отображение логических адресов на физические.
Рис. 4.7. Страничная трансляция адресов.
Не все 1024 таблицы страниц должны обязательно иметься в наличии (кстати, они заняли бы в памяти довольно много места — 4 Мбайт). Если программа реально использует лишь часть возможного линейного адресного пространства, а так всегда и бывает, то неиспользуемые поля в каталоге страниц помечаются, как отсутствующие. Для таких полей система, экономя память, не выделяет страничные таблицы.
При включенной страничной трансляции линейный адрес рассматривается, как совокупность трех полей: 10-битового индекса в каталоге страниц, 10-битовбго индекса в выбранной таблице страниц и 12-битового смещения в выбранной странице. Напомним, что линейный адрес образуется путем сложения базового адреса сегмента, взятого из дескриптора сегмента, и смещения в этом сегменте, предоставленного программой.
Старшие 10 бит линейного адреса образуют номер поля в каталоге страниц. Базовый адрес каталога хранится в одном из управляющих регистров процессора, конкретно, в регистре CR3. Из-за того, что каталог сам представляет собой страницу и выровнен в памяти на границу 4 Кбайт, в регистре CR3 для адресации к каталогу используются лишь старшие 20 бит, а младшие 12 бит зарезервированы для будущих применений.
Поля каталога имеют размер 4 байт, поэтому индекс, извлеченный из линейного адреса, сдвигается влево на 2 бит (т.е. умножается на 4) и полученная величина складывается с базовым адресом каталога, образуя адрес конкретного поля каталога. Каждое поле каталога содержит физический базовый адрес одной из таблиц страниц, причем, поскольку таблицы страниц сами представляют собой страницы и выровнены в памяти на границу 4 Кбайт, в этом адресе значащими являются только старшие 20 бит.
Далее из линейного адреса извлекается средняя часть (биты 12. 21), сдвигается влево на 2 бит и складывается с базовым адресом, хранящимся в выбранном поле каталога. В результате образуется физический адрес страницы в памяти, в котором опять же используются только старшие 20 бит. Этот адрес, рассматриваемый, как старшие 20 бит физического адреса адресуемой ячейки, носит название страничного кадра. Страничный кадр дополняется с правой стороны младшими 12 битами линейного адреса, которые проходят через страничный механизм без изменения и играют роль смещения внутри выбранной физической страницы.
Рассмотрим абстрактный пример, позволяющий проследить цепочку преобразования виртуального адреса в физический. Пусть программа выполняет командупри этом содержимое DS (селектор) составляет 1167И, а содержимое ЕВХ (смещение) 31678U.
Старшие 13 бит селектора (число 116U) образуют индекс дескриптора в системной дескрипторной таблице. Каждый дескриптор включает в себя довольно большой объем информации о конкретном сегменте и, в частности, его линейный адрес. Пусть в ячейке дескрипторной таблицы с номером 116h записан линейный адрес (базовый адрес сегмента) 0l0Sl000h.
Тогда полный линейный адрес адресуемой ячейки определится, как сумма базового адреса и смещения:Базовый адрес сегмента 0l0Sl000h
Смещение 0003167811
Полный линейный адрес 0108267811
При выключенной табличной трансляции величина 010826У811 будет представлять собой абсолютный физический адрес ячейки, содержимое которой должно быть прочитано приведенной выше командой mov. Легко сообразить, что эта ячейка находится в самом начале 17-го мегабайта оперативной памяти.
Посмотрим, как будет образовываться физический адрес при использовании страничной трансляции адресов. Полученный линейный адрес надо разделить на три составляющие для выделения индексов и смещения (рис. 4.8)Рис. 4.8. Пример линейного адреса.
Индекс каталога составляет 4h. Умножение его на 4 даст смещение от начала каталога. Это смещение равно 10h.
Индекс таблицы страниц оказался равным 82h. После умножения на 4 получаем смещение в таблице страниц, равное в данном случае 210h.
Предположим, что регистр CR3 содержит число S000h. Тогда физический адрес ячейки в каталоге, откуда надо получить адрес закрепленной за данным участком программы таблицы страниц, составит S000h + l0h = 8010h. Пусть по этому адресу записано число 4602lh. Его 12 младших битов составляют служебную информацию (в частности, бит 1 свидетельствует о присутствии этой таблицы страниц в памяти, а бит 5 говорит о том, что к этой таблице уже были обращения), а старшие биты, т.е. число 46000h образуют физический базовый адрес таблицы страниц. Для получения адреса требуемой ячейки этой таблицы к базовому адресу надо прибавить смещение 210h. Результирующий адрес составит 462101г.
Будем считать, что по адресу 4621011 записано число 01FF502111. Отбросив служебные биты, получим адрес физической страницы в памяти 01FF5000U. Этот адрес всегда оканчивается тремя нулями, так как страницы выровнены в памяти на границу 4 Кбайт. Для получения физического адреса адресуемой ячейки следует заполнить 12 младших бит полученного адреса битами смещения из линейного адреса нашей ячейки, в которых в нашем примере записано число 678h. В итоге получаем физический адрес памяти 01FF567811, расположенный в конце 32-го Мбайта.
Как видно из этого примера, и со страничной трансляцией, и без нее вычисление физических адресов адресуемых ячеек выполняется в защищенном режиме совсем не так, как в реальном. Неприятным практическим следствием правил адресации защищенного режима является уже упоминавшаяся «оторванность» прикладной программы от физической памяти. Программист, отлаживающий программу защищенного режима (например, приложение Windows), может легко заглянуть в сегментные регистры и определить селекторы, выделенные программе. Однако селекторы абсолютно ничего не говорят о физических адресах, используемых программой. Физические адреса находятся в таблицах дескрипторов, а эти таблицы недоступны прикладной программе. Таким образом, программист не знает, где в памяти находится его программа или используемые ею области данных.
С другой стороны, использование в процессе преобразования адресов защищенных системой таблиц имеет свои преимущества. Обычно многозадачная операционная система создает для каждой выполняемой задачи свой набор таблиц преобразования адресов. Это позволяет каждой из задач использовать весь диапазон виртуальных адресов, при этом, хотя для разных задач виртуальные адреса могут совпадать (и, как правило, по крайней мере частично совпадают), однако сегментное и страничное преобразования обеспечивают выделение для каждой задачи несовпадающих областей физической памяти, надежно изолируя виртуальные, адресные пространства задач друг от друга.
Вернемся теперь к таблицам дескрипторов и рассмотрим их более детально. Существует два типа дескрипторных таблиц: таблица глобальных дескрипторов (GDT от Global Descriptor Table) и таблицы локальных дескрипторов (LDT от Local Descriptor Table).Обычно для каждой из этих таблиц в памяти создаются отдельные сегменты, хотя в принципе это не обязательно. Таблица глобальных дескрипторов существует в единственном экземпляре и обычно принадлежит операционной системе, а локальных таблиц может быть много (это типично для многозадачного режима, в котором каждой задаче назначается своя локальная таблица).
Виртуальное адресное пространство делится на две равные половины. К одной половине обращение происходит через GDT, к другой половине через LDT. Как уже отмечалось, все виртуальное пространство состоит из 214 сегментов, из которых 213 сегментов адресуются через GDT, и еще 213 — чрез LDT.
Когда многозадачная система переключает задачи, глобальная таблица остается неизменной, а текущая локальная таблица заменяется на локальную таблицу новой задачи. Таким образом, половина виртуального пространства в принципе доступна всем задачам в системе, а половина переключается от одной задачи к другой по мере переключения самих задач.
Для программирования защищенного режима и даже для отладки прикладных программ, работающих в защищенном режиме, полезно представлять себе структуру дескриптора и смысл его отдельных полей. Следует заметить, что существует несколько типов дескрипторов, которым присущи разные форматы. Так, дескриптор сегмента памяти (наиболее распространенный тип дескриптора) отличается от дескриптора шлюза, используемого, в частности, для обслуживания прерываний. Рассмотрим формат дескриптора памяти (рис. 4.9).Рис. 4.9. Формат дескриптора памяти.
Как видно из рисунка, дескриптор занимает 8 байт. В байтах 2. 4 и 7 записывается линейный базовый адрес сегмента. Полная длина базового адреса — 32 бит. В байтах 0-1 записываются младшие 16 бит границы сегмента, а в младшие четыре бита байта атрибутов 2 — оставшиеся биты 16. 19. Границей сегмента называется номер его последнего байта. Мы видим, что граница описывается 20-ю битами, и ее численное значение не может превышать 1М. Однако, единицы, в которых задается граница, можно изменять, что осуществляется с помощью бита дробности G (бит 7 байта атрибутов 2). Если G=0, граница указывается в байтах; если 1 — в блоках по 4 Кбайт. Таким образом, размер сегмента можно задавать с точностью до байта, но тогда он не может быть больше 1 Мбайт; если же установить G=l, то сегмент может достигать 4 Гбайт, однако его размер будет кратен 4 Кбайт. База сегмента и в том, и в другом случае задастся с точностью до байта.
Рассмотрим теперь атрибуты сегмента, которые занимают два байта дескриптора.
Бит A (Accessed, было обращение) устанавливается процессором в тот момент, когда в какой-либо сегментный регистр загружается селектор данного сегмента. Далее процессор этот бит не сбрасывает, однако его может сбросить программа (разумеется, если она имеет доступ к содержимому дескриптора, что обычно является прерогативой операционной системы). Анализируя биты обращения различных сегментов, программа может судить о том, было ли обращение к данному сегменту’ после того, как она сбросила бит А.
Тип сегмента занимает 3 бит (иногда бит А включают в поле типа, и тогда тип занимает 4 бит) и может иметь 8 значений. Тип определяет правила доступа к сегменту. Так, если сегмент имеет тип 1, для него разрешены чтение и запись, что характерно для сегментов данных. Назначив сегменту тип 0, мы разрешим только чтение этого сегмента, защитив его тем самым от любых модификаций. Тип 4 обозначает разрешение исполнения, что характерно для сегментов команд. Используются и другие типы сегментов.
Подчеркнем, что защита сегментов памяти от несанкционированных его типом действий выполняется не программой, и даже не операционной системой, а процессором на аппаратном уровне. Так, при попытке записи в сегмент типа 0 возникнет так называемое исключение общей защиты. Исключением называется внутреннее прерывание, возбуждаемое процессором при возникновении каких-либо неправильных с его точки зрения ситуаций. Попытка записи в сегмент, для которого запись запрещена, и относится к такого рода ситуациям. Исключению общей защиты соответствует вектор 13, в котором должен находиться адрес обработчика этого исключения.
Стоит еще обратить внимание на тип 4. Для сегмента команд разрешается только исполнение, но не запись и даже не чтение. Это значит, что в защищенном режиме программа не может случайно залезть в свой сегмент команд и затереть его; не может она также и сознательно модифицировать команды в процессе своего выполнения — методика, иногда используемая в программах реального режима для защиты от их расшифровки любознательными программистами
Бит 4 байта атрибутов 1 является идентификатором сегмента. Если он равен 1, как это показано на рис. 4.9, дескриптор описывает сегмент памяти. Значение этого бита 0 характеризует дескриптор системного сегмента.
Поле DPL (Descriptor Privilege Level, уровень привилегий дескриптора) служит для защиты программ друг от друга. Уровень привилегий может принимать значения от 0 (максимальные привилегии) до 3 (минимальные). Программам операционной системы обычно назначается уровень 0, прикладным программам — уровень 3, в результате чего исключается возможность некорректным программам разрушить операционную систему. С другой стороны, если прикладная программа сама выполняет функции операционной системы, переводя процессор в защищенный режим и работая далее в этом режиме, ее сегментам следует назначить наивысший (нулевой) уровень привилегий, что откроет ей доступ ко всем средствам защищенного режима.
Бит Р говорит о присутствии сегмента в памяти. В основном он используется для организации виртуальной памяти. С помощью этого бита система может определить, находится ли требуемый сегмент в памяти, и при необходимости загрузить его с диска. В процессе выгрузки ненужного пока сегмента на диск бит Р в его дескрипторе сбрасывается.
Младшая половина байта атрибутов 2 занята старшими битами границы сегмента. Бит AVL (от Available, доступный) не используется и не анализируется процессором и предназначен для использования прикладными программами.
Бит D (Default, умолчание) определяет действующий по умолчанию размер для операндов и адресов. Он изменяет характеристики сегментов двух типов: исполняемых и стека. Если бит D сегмента команд равен 0, в сегменте по умолчанию используются 16-битовые адреса и операнды, если 1 — 32-битовые.
Атрибут сегмента, действующий по умолчанию, можно изменить на противоположный с помощью префиксов замены размера операнда (66h) и замены размера адреса (67п). Таким образом, для сегмента с D=0 префикс 66h перед некоторой командой заставляет ее рассматривать свои операнды, как 32-битовые, а для сегмента с D=l тот же префикс 66h, наоборот, сделает операнды 16-битовыми. В некоторых случаях транслятор сам включает в объектный модуль необходимые префиксы, в других случаях их приходится вводить в программу «вручную».
Рассмотрим теперь для примера простую программу, которая, будучи запущена обычным образом под управлением MS-DOS, переключает процессор в защищенный режим, выводит на экран для контроля символ, переходит назад в реальный режим (чтобы не вывести компьютер из равновесия) и завершается стандартным для DOS образом.
Для того, чтобы наша программа могла бы хоть что-то сделать в защищенном режиме, для нее необходимо создать среду защищенного режима, в первую очередь, таблицу глобальных дескрипторов с описанием всех сегментов, с которыми программа будет работать. Кроме нас никто эту таблицу (при работе в DOS) не создаст. Таким образом, наша программа будет в какой-то мере выполнять функции операционной системы защищенного режима.
Для практического исследования защищенного режима придется выполнить некоторую работу по переконфигурированию компьютера. В наше время компьютеры обычно конфигурируются так, что при их включении сразу загружается система Windows. Работы, для которых требуется DOS, выполняются либо в режиме эмуляции DOS, либо в сеансе DOS, организуемом системой Windows. Для запуска прикладной программы защищенного режима такой способ не годится. Нам понадобится DOS в «чистом виде», без следов Windows. Более того, перед запуском программы необходимо выгрузить все драйверы обслуживания расширенной памяти (HIMEM.SYS и EMM386.EXE) и программы, использующие расширенную память, например, SMARTDRV.EXE. Лучше всего загружать DOS с системной дискеты, подготовив файлы CONFIG.SYS и AUTOEXEC.BAT в минимальном варианте.
Обсуждая в начале этого раздела основы защищенного режима, мы не затронули многие, в том числе принципиальные вопросы, с которыми придется столкнуться при написании работоспособной программы. Необходимые пояснения будут даны в конце этого раздела.Пример 4-4. Программирование защищенного режима
.586Р ;Разрешение трансляции всех команд МП 586
;Структура для описания дескрипторов сегментов
dcr struc ;Имя структуры
limit dw 0 ;Граница (биты 0. 15)
base_l dw 0 ;База, биты 0. 15
base_m db 0 ;База, биты 16. 23
attr_l db 0 ;Байт атрибутов 1
attr_2 db ;Граница (биты 16. 19) и атрибуты 2
base_h db 0 ;База, биты 24. 31
dcr ends ;
data segment use16 ;
;Таблица глобальных дескрипторов GDT
gdt_null dcr ;Селектор 0-обязательный
;нулевой дескриптор
gdt_data dcr ;Селектор 8,
;сегмент данных
gdt_code dcr ;Селектор 16,
;сегмент команд
gdt_stack dcr ;Селектор 24 —
;сегмент стека
gdt_screen dcr ;Селектор 32,
;видеобуфер
pdescr df 0 ;Псевдодескриптор для команды Igdt
data_size=$-gdt_null ;Размер сегмента данных
data ends ;Конец сегмента данных
text segment use16 ;Сегмент команд, 16-разрядный режим
assume CS:text,DS:data;
main proc ;
xor EAX,EAX ;Очистим ЕАХ
mov AX,data ;Загрузим в DS сегментный
mov DS,AX ;адрес сегмента данных
;Вычислим 32-битовый линейный адрес сегмента данных
;и загрузим его в дескриптор сегмента данных в GDT.
;В регистре АХ уже находится сегментный адрес.
;Умножим его на 16 сдвигом влево на 4 бита
shl ЕАХ,4 ;В ЕАХ линейный базовый адрес
mov EBP, ЕАХ ;Сохраним его в ЕВР для будущего
mov BX,offset gdt_data ;В ВХ адрес дескриптора
mov [BX].base_l,AX ;Загрузим младшую часть базы
rol ЕАХ,16 ;Обмен старшей и младшей половин ЕАХ
mov [BX].base_m,AL ;Загрузим среднюю часть базы
;Вычислим 32-битовый линейный адрес -сегмента команд
;и загрузим его в дескриптор сегмента команд в GDT
хог ЕАХ, ЕАХ ;Очистим ЕАХ
mov AX,CS ;Сегментный адрес сегмента команд
shl ЕАХ,4 ;В ЕАХ линейный базовый адрес
mov BX,offset gdt_code ;В ВХ адрес дескриптора
mov [BX] .base_l,AX ;Загрузим младшую часть базы
rol ЕАХ,16 ;Обмен старшей и младшей половин ЕАХ
mov [BX].base_m,AL ;Загрузим среднюю часть базы
;Вычислим 32-битовый линейный адрес сегмента стека
хог ЕАХ, ЕАХ ;Все, как и для других
mov AX,SS ;дескрипторов
shl ЕАХ,4
mov BX,offset gdt_stack
mov [BX].base_l,AX
rol EAX,16
mov [BX].base_m,AL
;Подготовим псевдодескриптор pdescr для загрузки регистра GDTR
mov dword ptr pdescr+2,EBP ;База GDT
mov word ptr pdescr, 39 ;Граница GDT
Igdt pdescr ;Загрузим регистр GDTR
cli ;Запрет прерываний
;Переходим в защищенный режим
mov EAX,CR0 ;Получим содержимое CR0
or EAX,1 ;Установим бит защищенного режима
mov CRO,ЕАХ ;Запишем назад в CR0
;———————————————————;
;Теперь процессор работает в защищенном режиме ;
;———————————————————;
;Загружаем в CS:IP селектор:смещение точки continue
db OEAh ;Код команды far jmp
dw offset continue ;Смещение
dw 16 ;Селектор сегмента команд
continue:
;Делаем адресуемыми данные
mov AX, 8 ;Селектор сегмента данных
mov DS,AX ;Загрузим в DS
;Делаем адресуемым стек
mov AX,24 ;Селектор сегмента стека
mov SS,AX ;Загрузим в SS
;Инициализируем ES и выводим символ
mov AX,32 ;Селектор сегмента видеобуфера
mov ES,AX ;Загрузим в ES
mov BX,2000 ;Начальное смещение на экране
mov AX,09FOFh ;Символ с атрибутом
mov ES : [BX] , АХ;Вывод в видеобуфер
;Вернемся в реальный режим
mov gdt_data.limit,0FFFFh ;Установим
mov gdt_code.limit,0FFFFh ;значение границы
mov gdt_stack.limit,0FFFFh;для реального
mov gdt_screen.limit,0FFFFh ;режима
mov AX,8 ;Загрузим теневой регистр
mov DS,AX ;сегмента данных
mov AX,24 ;To же для
mov SS,AX ;стека
mov AX,32 ;To же
mov ES, AX ;для регистра ES
;Выполним дальний переход, чтобы заново загрузить
;селектор в CS и модифицировать его теневой регистр
db0Eah ;Код команды jmp far
dwoffset go ;Смещение точки перехода
dw!6 ;Селектор сегмента команд
;Переключим режим процессора
go: mov EAX,CR0 ;Получим содержимое CR0
and EAX,0FFFFFFFEh;Сбросим бит РЕ
mov CR0,EAX ;Запишем назад в CR0
db0Eah ; Код команды far jmp
dwoffset return ;Смещение точки перехода
dwtext ;Сегментный адрес
;———————————————;
;Теперь процессор снова работает в реальном режиме ;
;———————————————;
;Восстановим операционную среду реального режима
return: mov AX,data ;Загрузим сегментный
mov DS,AX ;регистр DS
mov AX,stk ;Загрузим сегментный
mov SS,AX ;регистр SS
mov SP,512 ;Восстановим SP
sti ;Разрешим прерывания
mov AX,4C00h ;Завершим программу
;обычным образом
int 2 In main endp
code_size=$-main ;Размер сегмента команд
text ends /Конец сегмента команд
stk segment stack ;Сегмент
db 512 dup (‘) ;стека stk ends
end main ;Конец программы и точка входаДля того, чтобы разрешить использование всех, в том числе привилегированных команд 32-разрядных процессоров, в программу включена директива .586Р.
Программа начинается с объявления структуры dcr, с помощью которой будут описываться дескрипторы сегментов. Сравнивая описание структуры dcr в программе с рис. 4.9, нетрудно проследить их соответствие друг другу. Для удобства программного обращения в структуре dcr база описывается тремя полями: младшим словом (base_l) и двумя байтами: средним (base_m) и старшим (base_h).
В байте атрибутов 1 задается ряд характеристик сегмента. В примере 4.4 используются сегменты двух типов: сегмент команд, для которого байт attr_l должен иметь значение 98h (присутствующий, только исполнение, DPL=0), и сегмент данных (или стека) с кодом 92h (присутствующий, чтение и запись, DPL=0).
Некоторые дополнительные характеристики сегмента указываются в старшем полубайте байта attr_2. Для всех наших сегментов значение этого полубайта равно 0 (бит G=0, так как граница указывается в байтах, а D=0, так как программа 16-разрядная).
Сегмент данных data начинается с описания важнейшей системной структуры — таблицы глобальных дескрипторов. Как уже отмечалось выше, обращение к сегментам в защищенном режиме возможно исключительно через дескрипторы этих сегментов. Таким образом, в таблице дескрипторов должно быть описано столько дескрипторов, сколько сегментов использует программа. В нашем случае в таблицу включены, помимо обязательного нулевого дескриптора, всегда занимающего первое место в таблице, четыре дескриптора для сегментов данных, команд, стека и дополнительного сегмента данных, который мы наложим на видеобуфер, чтобы обеспечить возможность вывода в него символов. Порядок дескрипторов в таблице (кроме нулевого) не имеет значения.
Поля дескрипторов для наглядности заполнены конкретными данными явным образом, хотя объявление структуры dcr с нулями во всех полях позволяет описать дескрипторы несколько короче, например:gdt_null dcr <> ;Селектор 0 — обязательный
;нулевой дескрипторgdt_data dcr ;Селектор 8 — сегмент данных
В дескрипторе gdt_data, описывающем сегмент данных программы, заполняется поле границы сегмента (фактическое значение размера сегмента data_size будет вычислено транслятором, см. последнее предложение сегмента данных), а также байт атрибутов 1. База сегмента, т.е. линейный адрес его начата, в явной форме в программе отсутствует, поэтому ее придется программно вычислить и занести в дескриптор уже на этапе выполнения.
Дескриптор gdt_codc сегмента команд заполняется схожим образом.
Дескриптор gdt_stack сегмента стека имеет, как и любой сегмент данных, код атрибута 92h, что разрешает его чтение и запись, и явным образом заданную границу — 255 байт, что соответствует размеру стека. Базовый адрес сегмента стека так же придется вычислить на этапе выполнения программы.
Последний дескриптор gdt_scrcen описывает страницу 0 видеобуфера. Размер видеостраницы, как известно, составляет 4096 байт, поэтому в поле границы указано число 4095. Базовый физический адрес страницы известен, он равен BS000h. Младшие 16 бит базы (число 8000И) заполняют слово base_l дескриптора, биты 16. 19 (число OBU) — байт base_m. Биты 20. 31 базового адреса равны 0, поскольку видеобуфер размещается в первом мегабайте адресного пространства.
Первая половина программы посвящена подготовке перехода в защищенный режим. Прежде всего надо завершить формирование дескрипторов сегментов программы, в которых остались незаполненными базовые адреса сегментов.
Базовые (32-битовые) адреса определяются путем умножения значений сегментных адресов на 16. После обнуления регистра ЕАХ и инициализации сегментного регистра DS, которая позволит нам обращаться к полям данных программы в реальном режиме, содержимое ЕАХ командой sill сдвигается влево на 4 бита, образуя линейный 32-битовый адрес. Поскольку этот адрес будет использоваться и в последующих фрагментах программы, он запоминается в регистре ЕВР (или любом другом свободном регистре общего назначения). В ВХ загружается адрес дескриптора данных, после чего в дескриптор заносится младшая половина линейного адреса из регистра АХ. Поскольку к старшей половине регистра ЕАХ (где нас интересуют биты 17. 24) обратиться невозможно, над всем содержимым ЕАХ с помощью команды rol выполняется циклический сдвиг на 16 бит, в результате которого младшая и старшая половины ЕАХ меняются местами.
После сдвига содержимое AL (где теперь находятся биты 17. 24 линейного адреса) заносится в поле base_m дескриптора. Аналогично Вычисляются линейные адреса сегмента команд и сегмента стека.
Следующий этап подготовки к переходу в защищенный режим — загрузка в регистр процессора GDTR (Global Descriptor Table Register, регистр таблицы глобальных дескрипторов) информации о таблице глобальных дескрипторов. Эта информация включает в себя линейный базовый адрес таблицы и ее границу и размещается в 6 байтах поля данных, называемого иногда псевдодескриптором. Для загрузки GDTR предусмотрена специальная привилегированная команда Igdt (load global descriptor table, загрузка таблицы глобальных дескрипторов), которая требует указания в качестве операнда имени псевдодескриптора. Формат псевдодескриптора приведен на рис. 4.10.Рис. 4.10. Формат псевдодескриптора.
В нашем примере заполнение псевдодескриптора упрощается вследствие того, что таблица глобальных дескрипторов расположена в начале сегмента данных, и ее базовый адрес совпадает с базовым адресом всего сегмента, который мы благоразумно сохранили в регистре ЕВР. Границу GDT в нашем случае легко вычислить в уме: 5 дескрипторов по 8 байт занимают объем 40 байт, и , следовательно, граница равна 39. Команда Igdt загружает регистр GDTR и сообщает процессору о местонахождении и размере GDT.
Еще одна важная операция, которую необходимо выполнить перед переходом в защищенный режим, заключается в запрете всех аппаратных прерываний. Дело в том, что в защищенном режиме процессор выполняет процедуру прерывания не так, как в реальном. При поступлении сигнала прерывания процессор не обращается к таблице векторов прерываний в первом килобайте памяти, как в реальном режиме, а извлекает адрес программы обработки прерывания из таблицы дескрипторов прерываний, построенной схоже с таблицей глобальных дескрипторов и располагаемой в программе пользователя (или в операционной системе). В примере 4.4 такой таблицы
нет, и па время работы нашей программы прерывания придется запретить. Запрет всех аппаратных прерываний осуществляется командой cli.
Теперь, наконец, можно перейти в защищенный режим, что делается на удивление просто. Для перевода процессора в защищенный режим достаточно установить бит 0 в управляющем регистре CRO. Всего в процессоре имеется 4 программно-адресуемых управляющих регистра с мнемоническими именами CRO, CR1, CR2 и CR3. Регистр CR1 зарезервирован, регистры CR2 и CR3 управляют страничным преобразованием, которое у нас выключено, а регистр CRO содержит целый ряд управляющих битов, из которых нас сейчас будут интересовать только биты 31 (разрешение страничного преобразования) и 0 (включение защиты). При включении процессора оба эти бита сбрасываются, и в процессоре устанавливается реальный режим с выключенным страничным преобразованием. Установка в 1 младшего бита CR0 переводит процессор в защищенный режим, сброс этого бита возвращает его в режим реальных адресов.
Для того, чтобы в процессе установки бита 0 не изменить состояние других битов регистра CR0, сначала его содержимое считывается командой mov в регистр ЕАХ, там с помощью команды or устанавливается младший бит, после чего второй командой mov измененное значение загружается в CR0. Процессор начинает работать по правилам защищенного режима.
Хотя защищенный режим установлен, однако действия по настройке системы еще не закончены. Действительно, во всех используемых в программе сегментных регистрах хранятся не селекторы дескрипторов сегментов, а базовые сегментные адреса, не имеющие смысла в защищенном режиме. Между прочим, отсюда можно сделать вывод, что после перехода в защищенный режим программа не должна работать, так как в регистре CS пока еще нет селектора сегмента команд, и процессор не может обращаться к этому сегменту. В действительности это не совсем так.
В процессоре для каждого из сегментных регистров имеется так называемый теневой регистр дескриптора, который имеет формат дескриптора (рис. 4.11). Теневые регистры недоступны программисту; они автоматически загружаются процессором из таблицы дескрипторов каждый раз, когда процессор загружает соответствующий сегментный регистр. Таким образом, в защищенном режиме программист имеет дело с селекторами, т.е. номерами дескрипторов, а процессор — с самими дескрипторами, хранящимися в теневых регистрах. Именно содержимое теневого регистра (в первую очередь, линейный адрес сегмента) определяет область памяти, к которой обращается процессор при выполнении конкретной команды.
В реальном режиме теневые регистры заполняются не из таблицы дескрипторов, а непосредственно самим процессором. В частности, процессор заполняет поле базы каждого теневого регистра линейным базовым адресом сегмента, полученным путем умножения па 16 содержимого сегментного регистра, как это и положено в реальном режиме. Поэтому после перехода в защищенный режим в теневых регистрах находятся правильные линейные базовые адреса, и программа будет выполняться правильно, хотя с точки зрения правил адресации защищенного режима содержимое сегментных регистров лишено смысла.Рис. 4.11. Сегментные регистры и теневые регистры дескрипторов.
Тем не менее после перехода в защищенный режим прежде всего следует загрузить в используемые сегментные регистры селекторы соответствующих сегментов. Это позволит процессору правильно заполнить все поля теневых регистров из таблицы дескрипторов. Пока эта операция не выполнена, некоторые поля теневых регистров (в частности, границы сегментов) содержат неверную информацию.
Загрузить селекторы в сегментные регистры DS, SS и ES не представляет труда. Но как загрузить селектор в регистр CS, в который запрещена прямая запись? Для этого можно воспользоваться искусственно сконструированной командой дальнего перехода, которая, как известно, приводит к смене содержимого и IP, и CS. Фрагментdb OEAh ;Код команды far jmp
dw offset continue ;Смещение
dw 16 ;Селектор сегмента команд
выглядящий совершенно нелепо в сегменте команд, как раз и демонстрирует эту методику. В реальном режиме мы поместили бы во второе слово адреса сегментный адрес сегмента команд, в защищенном же мы записываем в него селектор этого сегмента (число 16).
Команда дальнего перехода, помимо загрузки в CS селектора, выполняет еще одну функцию — она очищает очередь команд в блоке предвыборки команд процессора. Как известно, в современных процессорах с целью повышения скорости выполнения программы используется конвейерная обработка команд программы, позволяющая совместить во времени фазы их обработки. Одновременно с выполнением текущей (первой) команды осуществляется выборка операндов следующей (второй), дешифрация третьей и выборка из памяти четвертой команды. Таким образом, в момент перехода в защищенный режим уже могут быть расшифрованы несколько следующих команд и выбраны из памяти их операнды. Однако эти действия выполнялись, очевидно, по правилам реального, а не защищенного режима, что может привести к нарушениям в работе программы. Команда перехода очищает очередь предвыборки, заставляя процессор заполнить ее заново уже в защищенном режиме.
Далее выполняется загрузка в сегментные регистры DS и SS значений соответствующих селекторов, и на этом, наконец, заканчивается процедура перехода в защищенный режим.
Следующий фрагмент программы является, можно сказать, диагностическим. В нем инициализируется (по правилам защищенного режима!) сегментный регистр ES и в видеобуфер из регистра АХ выводится один символ. Код 0Fh соответствует изображению большой звездочки, а атрибут 9Fh — ярко-белому мерцающему символу на синем поле. Появление этого символа на экране служит подтверждением правильного функционирования программы в защищенном режиме.
Почему мы не предусмотрели вывод на экран хотя бы одной содержательной строки? Дело в том, что в защищенном режиме запрещены любые обращения к функциям DOS или BIOS. Причина этого совершенно очевидна — и DOS, и BIOS являются программами реального режима, в которых широко используется сегментная адресация реального режима, т.е. загрузка в сегментные регистры сегментных адресов. В защищенном же режиме в сегментные регистры загружаются не сегментные адреса, а селекторы. Кроме того, обращение к функциям DOS и BIOS осуществляется с помощью команд программного прерывания int с определенными номерами, а в защищенном режиме эти команды приведут к совершенно иным результатам. Поэтому в программе, работающей в защищенном режиме и не имеющей специальных и довольно сложных средств перехода в так называемый режим виртуального 86-го процессора, вывод на экран можно осуществить только прямым программированием видеобуфера. Нельзя также выполнить запись или чтение файла; более того, нельзя даже завершить программу средствами DOS. Сначала се надо вернуть в реальный режим.
Возврат в реальный режим можно осуществить разными способами. Мы воспользуемся для этого тем же регистром CRO, с помощью которого мы перевели процессор а защищенный режим. Казалось бы, для возврата в реальный режим достаточно сбросить бит 0 этого регистра. Однако дело обстоит не так просто. Для корректного возврата в реальный режим надо выполнить некоторые подготовительные операции, рассмотрение которых позволит нам глубже вникнуть в различия реального и защищенного режимов.
При работе в защищенном режиме в дескрипторах сегментов записаны, среди прочего, их линейные адреса и границы. Процессор при выполнении команды с адресацией к тому или иному сегменту сравнивает полученный им относительный адрес с границей сегмента и, если команда пытается адресоваться за пределами сегмента, формирует прерывание (исключение) нарушения общей защиты. Если в программе предусмотрена обработка исключений, такую ситуацию можно обнаружить и как то исправить. Таким образом, в защищенном режиме программа не может выйти за пределы объявленных ею сегментов, а также не может выполнить действия, запрещенные атрибутами сегмента. Так, если сегмент объявлен исполняемым (код атрибута 1 981т), то данные из этого сегмента нельзя читать или модифицировать; если атрибут сегмента равен 92h, то в таком сегменте не может быть исполняемых команд, на зато данные можно как читать, так и модифицировать. Указав для какого-то сегмента код атрибута 90h, мы получим сегмент с запрещением записи. При попытке записи в этот сегмент процессор сформирует исключение общей защиты.
Как уже отмечалось, дескрипторы сегментов хранятся в процессе выполнения программы в теневых регистрах (см. рис. 4.11), которые загружаются автоматически при записи в сегментный регистр селектора.
При работе в реальном режиме некоторые поля теневых регистров должны быть заполнены вполне определенным образом. В частности, поле границы любого сегмента должно содержать число FFFFh, а бит дробности сброшен. Следует подчеркнуть, что границы всех сегментов должны быть точно равны FFFFh; любое другое число, например, FFFEh, «не устроит» реальный режим.
Если мы просто перейдем в реальный режим сбросом бита 0 в регистре CR0, то в теневых регистрах останутся дескрипторы защищенного режима и при первом же обращении к любому сегменту программы возникнет исключение общей защиты, так как ни один из наших сегментов не имеет границы, равной FFFFh. Поскольку мы не обрабатываем исключения, произойдет либо сброс процессора и перезагрузка компьютера, либо зависание. Таким образом, перед переходом в реальный режим необходимо исправить дескрипторы всех наших сегментов: команд, данных, стека и видеобуфера К сегментным регистрам FS и GS мы не обращались, и о них можно не заботиться.
Теневые регистры, куда, собственно, надо записать значение границы, нам недоступны. Для из модификации придется прибегнуть к окольному маневру: записать в поля границ всех четырех дескрипторов значение FFFFh, а затем повторно загрузить селекторы в сегментные регистры, что приведет к перезаписи содержимого теневых регистров. С сегментным регистром CS так поступить нельзя, поэтому его загрузку придется выполнить, как и ранее, с помощью искусственно сформированной команды дальнего перехода.
Настроив все использовавшиеся в программе сегментные регистры, можно сбросить бит 0 в CR0. После перехода в реальный режим нам придется еще раз выполнить команду дальнего перехода, чтобы очистить очередь команд в блоке предвыборки и загрузить в регистр CS вместо хранящегося там селектора обычный сегментный адрес регистра команд.
Теперь процессор снова работает в реальном режиме, причем, хотя в сегментных регистрах DS, ES и SS остались незаконные для реального режима селекторы, программа будет какое-то время выполняться правильно, так как в теневых регистрах находятся правильные линейные адреса (оставшиеся от защищенного режима) и законные для реального режима границы (загруженные туда нами). Если, однако, в программе встретятся команды сохранения и восстановления содержимого сегментных регистров, напримервыполнение программы будет нарушено, так как команда pop DS загрузит в DS не сегментный адрес реального режима, а селектор, т.е. число 8 в нашем случае. Это число будет рассматриваться процессором, как сегментный адрес, и дальнейшие обращения к полям данных приведут к адресации физической памяти начиная с абсолютного адреса 80h, что, конечно, лишено смысла. Даже если в нашей программе нет строк сохранения и восстановления сегментных регистров, они неминуемо встретятся, как только произойдет переход в DOS по команде int 21h, так как диспетчер DOS сохраняет, а затем восстанавливает все регистры задачи, в том числе и сегментные. Поэтому после перехода в реальный режим необходимо загрузить в используемые далее сегментные регистры соответствующие сегментные адреса, что и выполняется в программе для регистров DS и SS. Надо также не забыть разрешить запрещенные нами ранее аппаратные прерывания (команда sti).
Можно еще заметить, что в той части программы, которая выполняется в защищенном режиме, не используется стек. Учитывая это, можно было несколько сократить текст программы, удалив из нее строки настройки регистра SS как при подготовке перехода в защищенный режим, так и при возврате в реальный. Не было также необходимости заново инициализировать указатель стека, так как его исходное содержимое — смещение дна стека, равное 512, никуда из SP не делось бы.
Программа завершается обычным образом функцией DOS 4Ch. Нормальное завершение программы и переход в DOS в какой-то мере свидетельствует о ее правильности.
У рассмотренной программы имеется серьезный недостаток — полное отсутствие средств отладки. Для отладки программ защищенного режима используется механизм прерываний и исключений, в нашей же программе этот механизм не активизирован. Поэтому всякие неполадки при работе в защищенном режиме, которые с помощью указанного механизма можно было бы обнаружить и проанализировать, в данном случае будут приводить к сбросу процессора.
В приведенном примере проиллюстрированы лишь базовые средства программирования защищенного режима: понятие селекторов и дескрипторов, создание глобальной таблицы дескрипторов, переход в защищенный режим и обратно, адресация в защищенном режиме. За кадром остались такие важные вопросы, как обработка исключений и аппаратных прерываний, уровни привилегий и защита по привилегиям, раздельные операционные среды и таблицы локальных дескрипторов, создание и взаимодействие задач, режим виртуального процессора 8086 и другие.Fore kc .ru
Рефераты, дипломы, курсовые, выпускные и квалификационные работы, диссертации, учебники, учебные пособия, лекции, методические пособия и рекомендации, программы и курсы обучения, публикации из профильных изданий
- если X = 1, то C является битом согласования и определяет, какие уровни привилегий могут перейти на этот сегмент (без изменения уровня привилегий):