§ Селектор

При адресации памяти в защищённом режиме команды ссылаются на сегменты, указывая не их адреса (как в режиме реальных адресов), а описания сегментов (их дескрипторы). Указатель на описание сегмента называется селектор. Другими словами, селектор - это номер дескриптора из таблицы дескрипторов.
Адресация производится через пару регистров сегмент:смещение, причём, в качестве сегментного регистра используются обычные CS, SS, DS, ES, FS и GS (последние два появились в 386-м процессоре), но в них указывается не адрес сегмента, а селектор дескриптора.
Селекторы нужны, по крайней мере, по трём причинам:
  • Описание сегмента занимает 8 байт и использовать 8-байтные сегментные регистры было бы крайне неэффективно
  • Селекторы имеют размер в 16 бит, благодаря чему их можно использовать в сегментных регистрах и обращаться к памяти можно по-прежнему через пару регистров
  • Параметры всех сегментов хранятся в отдельной области памяти, доступ к которой имеет только операционная система. Программа, используя селектор, сможет получить о сегменте совсем немного информации и не сможет изменить параметры сегмента, благодаря чему очень удачно реализуется механизм защиты
Адрес памяти можно указывать не только через пару регистров, но и в переменных, через пару значений селектор:смещение.
Можно было бы определить селектор просто, как номер сегмента, но в защищённом режиме кроме сегментов, дескриптор может определять целый ряд других системных объектов (например, задач), поэтому лучше не упрощайте понятия селектора и дескриптора, а постарайтесь привыкнуть к этой терминологии.
Селектор имеет следующий формат:
15 3 2 1 0
Index TI RPL
Двухбитовое поле RPL (Requested Privilege Level) содержит номер уровня привилегий, которое имеет текущая программа. Значение этого поля процессор использует для защиты по привилегиям. К одному и тому же дескриптору можно обращаться, используя селекторы с разными значениями RPL, но процессор позволит доступ только при определённых условиях (подробнее об этом см. в главе "Защита по привилегиям").
  • Бит TI (Table Indicator) определяет таблицу, из которой выбирается нужный дескриптор. Если бит TI = 0, то обращение производится к глобальной дескрипторной таблице GDT (она одна на всю систему), если TI = 1 - то к текущей локальной дескрипторной таблице LDT (таких может быть много). Подробнее дескрипторные таблицы обсуждаются в соответствующих главах.
  • Index - это собственной номер дескриптора, от 0 до 8191. Т.к. поле индекса состоит из 13 бит, то максимальное число дескрипторов, одновременно существующих в системе равно 213, т.е. 8192. Как видите, это довольно-таки много и вполне удовлетворяет любым системным запросам. На самом деле, число дескрипторов можно значительно увеличить за счёт использования множества дополнительных локальных дескрипторных таблиц.
Использование селекторов достаточно простое. Для тех дескрипторов, которые будут определены заранее, например, сегментов кода, стека и данных, селекторы подготавливаются как константы и затем используются для загрузки в сегментные регистры. Для дескрипторов, которые программа будет динамически создавать, селекторы придётся определять в переменных и загружать в сегментные регистры из памяти либо конструировать "на ходу", или даже как константы - всё зависит от конкретных условий. Способы использования селекторов и дескрипторов вы можете найти в примерах, которые будут следовать в дальнейших главах.
Обращение к дескрипторной таблице процессор производит только в момент загрузки в сегментный регистр нового селектора. После этого содержимое дескриптора копируется в так называемый "теневой регистр", к которому имеет доступ только сам процессор и из которого оно в дальнейшем используется. Любое последующее обращение к сегменту будет происходить с помощью теневого регистра, без обращения к дескрипторной таблице и не потребует лишних тактов на циклы чтения памяти. Правда, эти такты тратятся каждый раз, когда вы загружаете новый селектор, но это не высокая плата за защиту дескрипторов от недозволенного доступа.
При загрузке недопустимого значения селектора процессор будет генерировать исключение, даже если вы не обращались через него к памяти.