Дескрипторные таблицы В системах на базе Рх86 допускается создание практически любого числа сегментов и описывающих их дескрипторов. Область памяти, предназначенная для хранения 8-байтных дескрипторов, называется дескрипторной таблицей. Порядок размещения дескрипторов в таблице не играет роли, а максимальное число дескрипторов составляет 8192 ( т. е. максимальный размер дескрипторной таблицы равен 64Кбайт. В процессоре предусмотрено использование дескрипторных таблиц трех типов (Рис. . Выбор таблицы, в которую при создании сегмента помещается его дескриптор, зависит от назначения сегмента. Глобальная дескрипторная таблица (GDT) является главной общесистемной таблицей дескрипторов. Все программы (задачи), выполняющиеся в системе, могут использовать её для обращения к сегментам памяти (т.е. GDT разделяют («коллективно используют») все задачи. Для локализации таблицы в памяти и предназначен регистр GDTR, в котором находятся 32-битное поле линейного базового адреса таблицы и 16-битное поле предела с байтной грануляцией. Значение предела L связано с числом N дескрипторов в таблице простым соотношением L=8 * N – 1. Дескрипторная таблица прерываний (IDT) является также общесистемной и содержит дескрипторы специальных системных объектов, называемых шлюзами (gate), которые определяют точки входа процедур обработки прерываний и особых случаев. Иначе говоря, таблица IDT служит заменой таблицы векторов прерываний в реальном режиме. Для локализации таблицы в памяти и предназначен регистр IDTR, формат которого аналогичен формату регистра GDTR. Для перевода процессора из реального (R) режима в защищённый (P) необходимо создать таблицы GDTR и IDTR и соответственно инициализировать регистры GDTR и IDTR. Локальная дескрипторная таблица (LDT). В мультизадачной системе для каждой задачи в дополнение к таблице GDT можно построить свою локальную дескрипторную таблицу. Она определяет сегменты, доступные только этой конкретной задаче. Для локализации таблицы LDT предназначен регистр LDTR, который содержит только селектор, указывающий в таблице GDT нужный дескриптор и атрибуты таблицы LDT, которые становятся доступны процессору. В процессоре с регистром LDTR ассоциируется так называемый «теневой регистр», в котором и хранится дескриптор текущей задачи. При переключении с одной задачи на другую для замены используемой локальной дескрипторной таблицы достаточно загрузить в регистр LDTR селектор новой таблицы LDT , а процессор автоматически загрузит дескриптор новой LDT в теневой регистр. ОП Дескриптор N …. . . . . + 47 Дескриптор 1 0 Базовый адрес Предел Дескриптор 0 Регистр GDTR Дескриптор M + 47 …. . . . . Дескриптор 1 0 Предел G D T I D T Дескриптор 0 Базовый адрес Регистр IDTR Дескриптор K .. . . . . . . . + 15 0 Предел LDTR 47 Дескриптор 1 Дескриптор 0 Базовый адрес 0 Рис. 1 Дескрипторные таблицы и системные регистры L D T Селектор сегментов В защищенном режиме, также как и реальном, для доступа к сегментам используются сегментные регистры, однако в защищенном режиме содержимое сегментного регистра, называемое селектором, интерпретируется иначе. Селектор косвенно через дескрипторную таблицу определяет сегмент памяти. Новый формат селектора сегмента следующий: 15 2 1 0 TI RPL Index (индекс) Двухбитное поле запрашиваемого уровня привилегий RPL используется для контроля привилегий в механизме защиты процессора. Бит индикатора TI показывает из какой дескрипторной таблицы выбирается дескриптор: если TI = 0, обращение производится к глобальной дескрипторной таблице GDT, а при TI = 1 – к локальной дескрипторной таблице LDT. Старшие 13 бит селектора, образующие поле индекса Index, определяют нужный дескриптор в дескрипторной таблице. Процесс формирования линейного адреса памяти с помощью селектора и эффективного адреса показан на рис. 1. 15 0 Index 31 0 Эффективный адрес 0 63 0 63 GDT Права LDT База Права Предел База Предел 0 31 Базовый адрес сегмента + 31 0 Линейный адрес Рис. 1 Формирование линейного адреса памяти Так как дескриптор содержит 8 байт, поле индекса дополняется в процессоре тремя младшими нулями (умножается на 8). Значение селектора с полем Index = 0 и TI = 0 допустимо, но первый элемент таблицы GDT зарезервирован процессором и должен содержать нули. Такой селектор (Index = 0, TI = 0 и поле RPL произвольно) называется пустым селектором (null selector) или нуль- селектором, а соответствующий дескриптор – пустым дескриптором. Следовательно, пустые селекторы 0000Н – 0003Н не описывают никаких полезных сегментов. Пустой селектор разрешается загружать в сегментный регистр (кроме CS и SS), но любая попытка использовать его в формировании адреса вызывает особый случай. Рассмотрим процесс выполнения команды MOV EAX, [EBX][ESI + 8h] В это команде нет префикса замены сегмента, поэтому она обращается к текущему сегменту данных, селектор которого находится в регистре DS. Пусть в регистре DS содержится двоичное значение 00000000001010ххB. Бит TI = 0, следовательно, дескриптор сегмента находится в таблице GDT и имеет номер 5. Выполнение команды включает в себя следующее: 1. Образовать эффективный адрес ЕА = (ЕВХ) + (ESI) + 8 h 2. Выбрать пятый дескриптор из таблицы GDT. Для этого следует обратиться к полю базы регистра GDTR, прибавить к нему Index *8 из регистра DS и считать в процессор дескриптор по полученному адресу. 3. Просуммировать базовый адрес сегмента из дескриптора и эффективный адрес. В результате получится линейный (физический) адрес операнда. 4. Обратиться по линейному адресу к памяти и передать двойное слово в регистр ЕАХ. Описанные выше операции обращения к сегменту данных, требуют считывания из памяти 8-ми байтного дескриптора из таблицы GDT. (Отметим, что при обращении к сегменту через таблицу LDT, потребовалось бы дополнительное считывание из памяти дескриптора этой таблицы из таблицы GDT). Если производить все эти операции при каждом обращении к памяти (в том числе и при выборке каждой команды), производительность процессора будет невысокой. Чтобы не допустить этого в процессоре применяется кэширование (caching) дескрипторов. Кэширование опирается на тот факт, что обращение к памяти производится гораздо чаще, чем изменение сегментов и переключения задач. Поэтому целесообразно ускорить обращение к памяти за счёт замедления загрузки сегментных регистров. Компромиссное решение заключается в ассоциировании с каждым сегментным регистром (а также с регистрами LDTR и TR) «теневого» (shadow) регистра или кэшрегистра (рис.2). Такие кэш-регистры невидимы и явно недоступны программам. Когда программа загружает селектор в сегментный регистр, процессор автоматически считывает («кэширует») нужный дескриптор в соответствующий теневой регистр. Поскольку теперь дескриптор находится внутри процессора, для получения линейного адреса памяти потребуется сформировать эффективный адрес и просуммировать его с базовым адресом сегмента из нужного теневого регистра. Сегментные регистры 15 0 Регистры дескрипторов 64 0 CS SS DS ES FS GS Рис. 2 Теневые регистры дескрипторов сегментов Если программа редко модифицирует сегментные регистры, то в защищенном Р - режиме она будет выполняться примерно с такой же скоростью, как и в R – режиме. Загрузка и просмотр селекторов Процессор до загрузки селектора и кэширования дескриптора осуществляет несколько контрольных проверок. Некоторые из них связаны с контролем уровня привилегий в механизме защиты памяти, а остальные предотвращают загрузку бессмысленных селекторов. Процессор проверяет, что поле Index селектора находится в пределах таблицы, определяемой битом TI селектора сегмента. Именно в связи с этим пределы дескрипторных таблиц хранятся вместе с их базовыми адресами. При загрузке селектора в сегментный регистр данных (DS, ES, FS, GS) тип дескриптора должен разрешать считывание из сегмента ( сегменты с разрешенными операциями выполнения/считывания допустимы). В случае регистра SS в сегменте стека должны быть разрешены операции считывания и записи. При загрузке регистра CS сегмент должен быть обязательно исполняемым. Загрузка сегментного регистра и кэширование выбираемого селектором дескриптора осуществляется только при Р=1. В тех случаях, когда хотя бы одна проверка даёт отрицательный результат, формируется особый случай и загрузка селектора не производится. Для работы с сегментными регистрами доступны обычные команды MOV, PUSH и POP, а также специальные команды загрузки LDS, LES, LFS, LGS и LSS. При редактировании связей (компоновке) символическим адресам назначаются действительные (относительные) адреса памяти. Но окончательную привязку адресов осуществляет загрузчик. Манипуляции селекторами, дескрипторами и дескрипторными таблицами предоставлены разработчикам операционных систем и системным программистам. Для анализа селекторов предусмотрено несколько команд: LAR reg16/reg32, reg16/mem16 ; загрузка в регистр-получатель права доступа селектора, адресуемого вторым операндом. Т.е. возвращает байт прав доступа AR и биты G, D, X и U (расширение прав) из соответствующего селектору дескриптора сегмента. Анализируя соответствующие биты, можно определить, доступен ли данный сегмент программе, не нарушая защиты памяти; LSL reg16/reg32, reg16/mem16 ; по адресу первого операнда загружается предел сегмента, который определяет селектор, адресуемый вторым операндом. Если бит гранулярности G = 1 ( т.е. сегмент имеет страничную гранулярность), предел расширяется до 4Гб и регистрприёмник д.б. 32-х разрядным; VERR reg16/mem16 ; операнд задаёт селектор проверяемого сегмента и устанавливает бит ZF = 1, если программа может загрузить адресуемый селектор в сегментный регистр данных DS, ES, FS или GS, а также считывать из сегмента без нарушения защиты; VERW reg16/mem16 ; операнд задаёт селектор проверяемого сегмента и устанавливает бит ZF = 1, если программа может загрузить адресуемый селектор в сегментный регистр данных DS, ES, FS или GS, а также записывать в сегмент без нарушения защиты. Обе последние команды не контролируют бит Р в дескрипторе (присутствие сегмента в памяти).