Как уже отмечалось, адрес, вычисляемый процессором на основе селектора и смещения, относится к линейном)' адресному' пространству, не обязательно совпадающему с физическим. Преобразование линейных адресов в физические осущсстатяется с помощью так называемой страничной трансляции, частично реализуемой процессором, а частично — one-
усширенные возможности современных микропроцессоров
щионной системой. Если страничная трансляция выключена, все ли-£ейные адреса в точности совпадают с физическими; если страничная >ансляция включена, то линейные адреса преобразуются в физические в этветствии с содержимым страничных таблиц (рис. 4.5).
15 0 31 0 | образование рипторов) | 31 0 | >бразоваиис 1 ранни) | 31 0 |
Селектор; Смещение — > | * * | -> -> | 8-2 с 3 | -» |
Виртуальный адрес | Сегментпо (таблицы | Линейный адрес | 2? = о IE & и | Физический адрес * |
Рис. 4.5. Цепочка преобразований виртуального адреса в физический.
Страницей называется связный участок линейного или физического адресного пространства объемом 4 Кбайт. Программа работает в линейном адресном пространстве, не подозревая о существовании страничного преобразования или даже самих страниц. Механизм страничной трансляции отображает логические страницы на физические в соответствии с информацией, содержащейся в страничных таблицах. В результате отдельные 4х-килобайтовые участки программы могут реально находиться в любых несвязных друг с другом 4х-килобайтовых областях физической памяти (рис. 4.6). Порядок размещения физических страниц в памяти может не соответствовать (и обычно не соответствует) порядку следования логических страниц. Более того, некоторые логические страницы могут перекрываться, фактически сосуществуя в одной и той же области физической памяти.
Страничная трансляция представляет собой довольно сложный механизм, в котором принимают участие аппаратные средства процессора и находящиеся в памяти таблицы преобразования. Назначение и взаимодействие элементов системы страничной трансляции схематически изображено на рис. 4.7.
Система страничных таблиц состоит из двух уровней. На первом уровне находится каталог страниц — резидентная в памяти таблица, содержащая 1024 4х-байтовых поля с адресами таблиц страниц. На втором уровне находятся таблицы страниц, каждая из которых содержит так же 1024 4х-байтовых поля с адресами физических страниц памяти. Максимально возможное число таблиц страниц определяется числом полей в каталоге и может доходить до 1024. Поскольку размер страницы составляет 4 Кбайт, 1024 таблицы по 1024 страницы перекрывают все адресное пространство (4 Гбайт).
Глава 4
![]() |
4 Кбайт
4 Кбайт
4 Кбайт
4 Кбайт
4 Кбайт
4 Кбайт
4 Кбайт
4 Кбайт
Линейное адресное пространство
Физическое адресное пространство
Рис. 4.6. Отображение логических адресов на физические.
Страничный кадр |
Каталог страниц 1024 4х-байтовых полей |
Линейный адрес
2221 12 Ц
Физический адрес
12 11
Смещение
Таблица страниц
CR3
Каждая таблица содержит 1024 4х-байтовых полей
Базовый адрес каталога
Рис. 4.7. Страничная трансляция адресов.
расширенные возможности современных микропроцессоров_____________________ 181
Не все 1024 таблицы страниц должны обязательно иметься в наличии (кстати, они заняли бы в памяти довольно много места — 4 Мбайт). Если программа реально использует лишь часть возможного линейного адресного пространства, а так всегда и бывает, то неиспользуемые поля в каталоге страниц помечаются, как отсутствующие. Для таких полей система, экономя память, не выделяет страничные таблицы.
При включенной страничной трансляции линейный адрес рассматривается, как совокупность трех полей: 10-битового индекса в каталоге страниц, 10-битового индекса в выбранной таблице страниц и 12-битового смещения в выбранной странице. Напомним, что линейный адрес образуется путем сложения базового адреса сегмента, взятого из дескриптора сегмента, и смещения в этом сегменте, предоставленного программой.
Старшие 10 бит линейного адреса образуют номер поля в каталоге страниц. Базовый адрес каталога хранится в одном из управляющих регистров процессора, конкретно, в регистре CR3. Из-за того, что каталог сам представляет собой страницу и выровнен в памяти на границу 4 Кбайт, в регистре CR3 для адресации к каталогу используются лишь старшие 20 бит, а младшие 12 бит зарезервированы для будущих применений.
Поля каталога имеют размер 4 байт, поэтому индекс, извлеченный из линейного адреса, сдвигается влево на 2 бит (т.е. умножается на 4) и полученная величина складывается с базовым адресом каталога, образуя адрес конкретного поля каталога. Каждое поле каталога содержит физический базовый адрес одной из таблиц страниц, причем, поскольку таблицы страниц сами представляют собой страницы и выровнены в памяти на границу 4 Кбайт, в этом адресе значащими являются только старшие 20 бит.
Далее из линейного адреса извлекается средняя часть (биты 12...21), сдвигается влево на 2 бит и складывается с базовым адресом, хранящимся в выбранном поле каталога. В результате образуется физический адрес страницы в памяти, в котором опять же используются только старшие 20 бит. Этот адрес, рассматриваемый, как старшие 20 бит физического адреса адресуемой ячейки, носит название страничного кадра. Страничный кадр дополняется с правой стороны младшими 12 битами линейного адреса, которые проходят через страничный механизм без изменения и играют роль смещения внутри выбранной физической страницы.
Рассмотрим абстрактный пример, позволяющий проследить цепочку преобразования виртуального адреса в физический. Пусть программа выполняет команду
mov EAX,DS:[EBX]
при этом содержимое DS (селектор) составляет 1167h, а содержимое ЕВХ (смещение) 31678h.
Старшие 13 бит селектора (число 116U) образуют индекс дескриптора в системной дескрипторной таблице. Каждый дескриптор включает в себя довольно большой объем информации о конкретном сегменте и, в частности, его линейный адрес. Пусть в ячейке дескрипторной таблицы с номером 116h записан линейный адрес (базовый адрес сегмента) 01051000h.
Глава -t
Тогда полный линейный адрес адресуемой ячейки определится, как сумма базового адреса и смещения:
Базовый адрес сегмента 01051000h
Смещение 00031678h
Полный линейный адрес 01082678h
При выключенной табличной трансляции величина 01082678U будет представлять собой абсолютный физический адрес ячейки, содержимое которой должно быть прочитано приведенной выше командой mov. Легко сообразить, что эта ячейка находится в самом начале 17-го мегабайта оперативной памяти.
Посмотрим, как будет образовываться физический адрес при использовании страничной трансляции адресов. Полученный линейный адрес надо разделить на три состаатяющис для выделения индексов и смещения (рис. 4.8)
22 21
О
| 0000000100 I 0010000010 | 01100111 1000 I
Индекс каталога Индекс таблицы Смещение
страниц Рис, 4.8. Пример линейного адреса.
Индекс каталога составляет 4h. Умножение его на 4 даст смещение от начала каталога. Это смещение равно 10h.
Индекс таблицы страниц оказался равным 82h. После умножения на 4 получаем смещение в таблице страниц, равное в данном случае 210h.
Предположим, что регистр CR3 содержит число 80001г. Тогда физический адрес ячейки в каталоге, откуда надо получить адрес закрепленной за данным участком программы таблицы страниц, составит SOOOh + 10h = 8010h. Пусть по этому адресу записано число 460211г. Его 12 младших битов составляют служебную информацию (в частности, бит 1 свидетельствует о присутствии этой таблицы страниц в памяти, а бит 5 говорит о том, что к этой таблице уже были обращения), а старшие биты, т.е. число 46000h образуют физический базовый адрес таблицы страниц. Для получения адреса требуемой ячейки этой таблицы к базовому адресу надо прибавить смещение 210U. Результирующий адрес составит 462101г.
Будем считать, что по адресу 46210h записано число 01FF5021h. Отбросив служебные биты, получим адрес физической страницы в памяти 01FF5000U. Этот адрес всегда оканчивается тремя нулями, так как страницы выровнены в памяти на границу' 4 Кбайт. Для получения физического адреса адресуемой ячейки следует заполнить 12 младших бит полученного адреса битами смещения из линейного адреса нашей ячейки, в которых в нашем примере записано число 678U. В итоге получаем физический адрес памяти 01FF5678h, расположенный в конце 32-го Мбайта.
Как видно из этого примера, и со страничной трансляцией, и без нее вычисление физических адресов адресуемых ячеек выполняется в защищенном режиме совсем не так, как в реальном. Неприятным практичес-
расширенные возможности современных микропроцессоров
ким следствием правил адресации защищенного режима является уже упоминавшаяся «оторванность» прикладной программы от физической памяти. Программист, отлаживающий программу защищенного режима (например, приложение Windows), может легко заглянуть в сегментные регистры и определить селекторы, выделенные программе. Однако селекторы абсолютно ничего не говорят о физических адресах, используемых программой. Физические адреса находятся в таблицах дескрипторов, а эти таблицы недоступны прикладной программе. Таким образом, программист не знает, где в памяти находится его программа или используемые ею области данных.
С другой стороны, использование в процессе преобразования адресов защищенных системой таблиц имеет свои преимущества. Обычно многозадачная операционная система создает для каждой выполняемой задачи свой набор таблиц преобразования адресов. Это позволяет каждой из задач использовать весь диапазон виртуальных адресов, при этом, хотя для разных задач виртуальные адреса могут совпадать (и, как правило, по крайней мере частично совпадают), однако сегментное и страничное преобразования обеспечивают выделение для каждой задачи несовпадающих областей физической памяти, надежно изолируя виртуальные адресные пространства задач друг от друга.
Вернемся теперь к таблицам дескрипторов и рассмотрим их более де-тально. Существует два типа дескрипторных таблиц: таблица глобальных дескрипторов (GDT от Global Descriptor Table) и таблицы локальных дескрипторов (LDT от Local Descriptor Table).Обычно для каждой из этих таблиц в памяти создаются отдельные сегменты, хотя в принципе это не обязательно. Таблица глобальных дескрипторов существует в единственном экземпляре и обычно принадлежит операционной системе, а локальных таблиц может быть много (это типично для многозадачного режима, в котором каждой задаче назначается своя локальная таблица).
Виртуальное адресное пространство делится на две равные половины. К одной половине обращение происходит через GDT, к другой половине через LDT. Как уже отмечалось, все виртуальное пространство состоит из 214 сегментов, из которых 213 сегментов адресуются через GDT, и еще 213 - чрез LDT.
Когда многозадачная система переключает задачи, глобальная таблица остается неизменной, а текущая локальная таблица заменяется на локальную таблицу новой задачи. Таким образом, половина виртуачьного пространства в принципе доступна всем задачам в система, а половина переключается от одной задачи к другой по мере переключения самих задач.
Для программирования защищенного режима и даже для отладки прикладных программ, работающих в защищенном режиме, полезно представлять себе структуру дескриптора и смысл его отдельных полей. Следует заметить, что существует несколько типов дескрипторов, которым присущи разные форматы. Так, дескриптор сегмента памяти (наиболее распространенный тип дескриптора) отличается от дескриптора шлюза, используемого, в частности, для обслуживания прерываний. Рассмотрим формат дескриптора памяти (рис. 4.9).
Глава -f
Байты | ||||||||
.6 5 | ||||||||
База 31...24 | •\триб\ты 2' | Атрибуты Г | База сегмента 23...0 rP™mja сегмента i i i | |||||
Биты 765 | /< 43210 | Ч Биты 43210 | ||||||
G | D | А V L | 1 1 1 Граница 19...16 i i i | i Р DPL | Тип i i | А |
Рис. 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 обозначает разрешение исполнения, что характерно для сегментов команд. Используются и другие типы сегментов.
Расширенные возможности современных микропроцессоров_____________________ 185
Подчеркнем, что защита сегментов памяти от несанкционированных его типом действий выполняется не программой, и даже не операционной системой, а процессором на аппаратном уровне. Так, при попытке записи в сегмент типа 0 возникнет так называемое исключение общей защиты. Исключением называется внутреннее прерывание, возбуждаемое процессором при возникновении каких-либо неправильных с его точки зрения ситуаций. Попытка записи в сегмент, для которого запись запрещена, и относится к такого рода ситуациям. Исключению общей защиты соответствует вектор 13, в котором должен находиться адрес обработчика этого исключения.
Стоит еще обратить внимание на тип 4. Для сегмента команд разрешается только исполнение, но не запись и даже не чтение. Это значит, что в защищенном режиме программа не может случайно залезть в свой сегмент команд и затереть его; не может она также и сознательно модифицировать команды в процессе своего выполнения — методика, иногда используемая в программах реального режима для защиты от их расшифровки любознательными программистами.
Бит 4 байта атрибутов 1 является идентификатором сегмента. Если он равен 1, как это показано на рис. 4.9, дескриптор описывает сегмент памяти. Значение этого бита 0 характеризует дескриптор системного сегмента.
Поле DPL (Descriptor Privilege Level, уровень привилегий дескриптора) служит для защиты программ друг от друга. Уровень привилегий может принимать значения от 0 (максимальные привилегии) до 3 (мини-матьные). Программам операционной системы обычно назначается уровень 0, прикладным программам — уровень 3, в результате чего исключается возможность некорректным программам разрушить операционную систему. С другой стороны, если прикладная программа сама выполняет функции операционной системы, переводя процессор в защищенный режим и работая далее в этом режиме, ее сегментам следует назначить наивысший (нулевой) уровень привилегий, что откроет ей доступ ко всем средствам защищенного режима.
Бит Р говорит о присутствии сегмента в памяти. В основном он ис-
|j пользуется для организации виртуальной памяти. С помощью этого бита
система может определить, находится ли требуемый сегмент в памяти, и
при необходимости загрузить его с диска. В процессе выгрузки ненужного
пока сегмента на диск бит Р в его дескрипторе сбрасывается.
Младшая половина байта атрибутов 2 занята старшими битами границы сегмента. Бит AVL (от Available, доступный) не используется и не анализируется процессором и предназначен для использования прикладными программами.
Бит D (Default, умолчание) определяет действующий по умолчанию
размер для операндов и адресов. Он изменяет характеристики сегментов
двух типов: исполняемых и стека. Если бит D сегмента команд равен 0, в
^сегменте по умолчанию используются 16-битовые адреса и операнды, если
1 — 32-битовые.
Глава 4
Атрибут сегмента, действующий по умолчанию, можно изменить на противоположный с помощью префиксов замены размера операнда (66h) и замены размера адреса (67h). Таким образом, для сегмента с D=0 префикс 66U перед некоторой командой заставляет ее рассматривать свои операнды, как 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 base_ldw basc_m db attr Idb |
;Имя структуры;Граница (биты 0...15);База, биты 0...15;База, биты 16...23;Байт атрибутов 1
уширенные возможности современных микропроцессоров
;Граница (биты 16...19) и атрибуты 2;База, биты 24...31 |
attr_2db О
baseji db О
dcr ends;
data segment use!6;
;Таблица глобальных деифипторов GOT
gdt_null dcr <0,0,0,0,0,0>;Селектор О -обязательный
;нулевой дескриптор
gdt_data dcr <data_size-l,0,0,92h,0,0>;Селектор 8,
;сегмент данных
gdt_code dcr <сос!е_817е-1,0,0,98п,0,0>;Селектор 16,
;сегмент команд
gdt_stack dcr <511,0,0,92h,0,0>;Селектор 24 —
;сегмент стека
gdt_screen dcr <4095,8000h,OBh,92h,0,0>;Селектор 32,
видеобуфер
pdescr df 0; Псевдодескриптор для команды Igdt
data_size=:S-gdt_iiull;Размер сегмента данных
data ends;Конец сегмента данных
text segment use!6;Сегмент команд, 16-разрядный режим
assume CS:text,DS:data;
main proc;
xor EAX,EAX;Очистим ЕАХ
mov AX,data;3агрузим в DS сегментный
mov DS,AX;адрес сегмента данных
;Вычислим 32-битовый линейный адрес сегмента данных
;и загрузим его в дескриптор сегмента данных в GDT.
;В регистре АХ уже находится сегментный адрес.
;Умножим его на 16 сдвигом влево на 4 бита
shl ЕАХ,4;В ЕАХ линейный базовый адрес
mov ЕВР,ЕАХ;Сохраним его в ЕВР для будущего
mov BX,offset gdt_data;B BX адрес дескриптора
mov [BX].base_I,AX;3агрузим младшую часть базы
rol ЕАХ,16;Обмен старшей и младшей половин ЕАХ
mov [BX].base_m,AL;3arpy3HM среднюю часть базы
;Вычислим 32-битовый линейный адрес сегмента команд
;и загрузим его в дескриптор сегмента команд в GDT
хог ЕАХ,ЕАХ;Очистим ЕАХ
mov AX,CS;Сегментный адрес сегмента команд
shl ЕАХ,4;В ЕАХ линейный базовый адрес
mov BX,offset gdt_code;В ВХ адрес дескриптора.
mov [BX].base_I,AX;3arpy3HM младшую часть базы
rol ЕАХ, 16;Обмен старшей и младшей половин ЕАХ
mov [BX].base_m,AL;3arpy3HM среднюю часть базы
;Вьгчислим 32-битовый линейный адрес сегмента стека
хог ЕАХ,ЕАХ;Все, как и для других
mov AX,SS дескрипторов
shl EAX,4
Глава -t
mov BX,offset gdt_stack
mov {BX].base_l,AX
rol ЕАХД6
mov [BX].base_m,AL
;Подготовим псевдодескриптор pdescr для загрузки регистра GDTR
mov dword ptr pdescr+2,EBP;База GOT
mov word ptr pdescr,39;ГраницаООТ
Igdt pdescr;3агрузим регистр GDTR
cli;3апрет прерываний
; Переходим в защищенный режим
mov EAX,CRO;Получим содержимое CRO
or EAX,1;Установим бит защищенного режима
mov CRO,EAX;3апишем назад в CRO
; Теперь процессор работает в защищенном режиме;
?,
;3агружаем в CS:IP селектор смещение точки continue
db OEAh;Код команды far jmp
dw offset continue;Смещение
dw 16;Селектор сегмента команд
continue:;Делаем адресуемыми данные
mov АХ,8;Селектор сегмента данных
mov DS,AX;3агрузим в DS
;Делаем адресуемым стек
mov mov |
АХ,24 SS,AX |
;Селектор сегмента стека;3агрузим в SS
Инициализируем ES и вьшодим символ
mov AX,32;Селектор сегмента видеобуфера
mov ES,AX;3агрузим в ES
mov BX,2000;Начальное смещение на экране
mov AX,09FOFh;Символ с атрибутом mov ES:[BX],AX;Вывод в видеобуфер;Вернемся в реальный режим
; Установим;значение границы;для реатъного;режима ;3агрузим теневой регистр ;сегмента данных |
mov gdt_data.limit,OFFFFh mov gdt_code.limit,OFFFFh
mov mov mov mov mov mov mov mov |
gdt_stack.limit,OFFFFh
gdt_screen. limit, OFFFFh
AX, 8
DS,AX
;То же для ; стека ;То же ;для регистра ES |
АХ,24 SS,AX АХ,32 ES,AX
;Выполним дальний переход, чтобы заново загрузить;селектор в CS и модифицировать его теневой регистр
Расширенные возможности современных микропроцессоров
db OEah;Код команды jmp far
dw offset go;Смещение точки перехода
dw 16;Селектор сегмента команд
;Переключим режим процессора
go: mov EAX,CRO;Получим содержимое CRO
and EAX,OFFFFFFFEh;C6pocHM бит РЕ
mov CRO,EAX;3апишем назад в CRO
db OEah;Код команды far jmp
dw offset return;Смещение точки перехода
dw text;Сегментный адрес
^ ^
; Теперь процессор снова работает в реальном режиме;
^ ^
; Восстановим операционную среду реального режима
return: mov AX,data mov DS,AX mov AX,stk SS,AX SP,512 |
mov mov sti mov |
AX.4COOU |
;3агрузим сегментный;регистр DS;3агрузим сегментный;регистр SS восстановим SP;Разрешим прерывания;3авершим программу;обычным образом
2Ih
int
;Размер сегмента команд; Конец сегмента команд; Сегмент; стека |
main endp code_size=S -main text ends stk segment stack
db 512 dup О stk ends
;Конец программы и точка входа |
Для того, чтобы разрешить использование всех, в том числе привилегированных команд 32-разрядных процессоров, в программу включена директива.586Р. Программа начинается с объявления структуры dcr, с помощью которой будут описываться дескрипторы сегментов. Сравнивая описание структуры dcr в программе с рис. 4.9, нетрудно проследить их соответствие друг другу. Для удобства программного обращения в структуре dcr база описывается тремя полями: младшим словом (base_l) и двумя байтами: средним (base_m) и старшим (base_h). В байте атрибутов 1 задается ряд характеристик сегмента. В примере 4.4 используются сегменты двух типов: сегмент команд, для которого байт attr_l должен иметь значение 98h (присутствующий, только исполнение, DPL=0), и сегмент данных (или стека) с кодом 92п (присутствующий, чтение и запись, DPL=0). Некоторые дополнительные характеристики сегмента указываются в старшем полубайте байта attr_2. Для всех наших сегментов значение этого |
end main
Глава 4
полубайта равно 0 (бит G=0, так как граница указывается в байтах, а D=0, так как программа 16-разрядная).
Сегмент данных data начинается с описания важнейшей системной структуры — таблицы глобальных дескрипторов. Как уже отмечалось выше, обращение к сегментам в защищенном режиме возможно исключительно через дескрипторы этих сегментов. Таким образом, в таблице дескрипторов должно быть описано столько дескрипторов, сколько сегментов использует программа. В нашем случае в таблицу включены, помимо обязательного нулевого дескриптора, всегда занимающего первое место в таблице, четыре дескриптора для сегментов данных, команд, стека и дополнительного сегмента данных, который мы наложим на видеобуфер, чтобы обеспечить возможность вывода в него символов. Порядок дескрипторов в таблице (кроме нулевого) не имеет значения.
Поля дескрипторов для наглядности заполнены конкретными данными явным образом, хотя объявление структуры dcr с нулями во всех полях позволяет описать дескрипторы несколько короче, например:
gdt_null dcr <>
gdt_datadcr <data_size-l,,,92h>
;Селектор 0 — обязательный;нулевой дескриптор;Селектор 8 — сегмент данных
В дескрипторе gdt_data, описывающем сегмент данных программы, заполняется поле границы сегмента (фактическое значение размера сегмента data_size будет вычислено транслятором, см. последнее предложение сегмента данных), а также байт атрибутов 1. База сегмента, т.е. линейный адрес его начала, в явной форме в программе отсутствует, поэтому ее придется программно вычислить и занести в дескриптор уже на этапе выполнения.