§ Локальные дескрипторные таблицы
Локальная дескрипторная таблица LDT (Local Descriptor Table) является аналогом глобальной дескрипторной таблицы GDT и предназначена для применения в контексте задачи. Таким образом, задача может иметь свой собственный набор дескрипторов, непосредственный доступ к которым имеет только она сама; операционная система также может использовать локальные дескрипторы задачи, но явного механизма для этого в процессоре не предусмотрено и это зависит от конкретной системы. Благодаря локальной дескрипторной таблице, которые мы далее будем называть просто LDT, и грамотному построению ядра ОС можно гарантировать, что кроме данной задачи никакая другая не сможет воспользоваться её дескрипторами, что, несомненно, повышает защищённость системы.LDT имеет точно такую же структуру, как и GDT; в ней так же нельзя использовать нулевой дескриптор и всё же LDT имеет ряд отличий от GDT:
- В LDT нельзя использовать дескрипторы некоторых системных объектов, подробнее об этом вы узнаете далее.
- LDT можно использовать только в контексте задачи. Для использования задач необходима инициализация некоторых дескрипторов и структур в памяти, которые собственно и определяют контекст задачи, поэтому просто так, в обычной процедуре, подобной тем, что мы до сих пор использовали, LDT применить нельзя.
- Одновременно в задаче можно использовать только одну LDT, зато их можно менять на другие, т.е. переключаться с одной LDT на другую, однако это только теоретическое использование LDT, реально необходимости в её смене не возникает.
Давайте вспомним формат селектора:
Обратите внимание на 2-й бит селектора. Он называется TI (Table Indicator) и определяет, к какой дескрипторной таблице производится обращение - если TI = 0, то к GDT, если TI = 1 - к LDT.
До сих пор мы использовали только нулевое значение этого бита, кроме того, поле RPL селектора мы также везде устанавливали нулевым, отсюда и получались значения селекторов, кратные восьми: 8 - для кода, 16 - для стека, 24 - для основного сегмента данных и т.д.; с использованием задач нам станут доступны все возможные значения селекторов.
Итак, селектор, имеющий одинаковый индекс, позволяет выбирать дескриптор как из GDT, так из LDT. Например, для выборки первого дескриптора из таблицы (нулевой дескриптор не используется!) нужно, чтобы поле индекса селектора содержало единицу (1). Если предположить, что поле PRL будет нулевым (как мы до сих пор и делали), то для выборки первого дескриптора:
- из GDT, нам понадобится селектор со значением 1000b;
- а из LDT - селектор со значением 1100b.
Практически, при обращении к LDT (опять же подразумевая, что поле RPL в селекторе будет содержать 0), можно определить селекторы, например, так:
Для GDT:
1Code_Selector = 8 2Data_Selector = 16 3Stack_Selector = 24Для LDT:
1LDT_Code_Selector = 8 + 100b 2LDT_Data_Selector = 16 + 100b 3LDT_Stack_Selector = 24 + 100bЛокальная дескрипторная таблица представляет собой по сути массив 8-байтных дескрипторов, однако, для использования LDT нужно определить специальный дескриптор системного объекта - дескриптор LDT. Этот дескриптор имеет следующий формат:
1 dw limit_low ; Младшая часть предела 2 dw address_low ; Младшая часть базового адреса 3 db address_mid ; Третий (из четырёх) байт базового адреса 4 db access_rights ; Права доступа 5 db limit_hi ; Старшая часть предела 6 db address_mid ; Четвёртый (старший) байт базового адресаБолее наглядно это показано на рисунке:
Как видите, формат дескриптора LDT очень похож на формат обычного дескриптора сегмента данных или кода, за исключением содержимого байта прав доступа и нулей вместо бит GDXU (старшие 4 бита в байте со старшей частью предела) - здесь эти биты зарезервированы и должны быть равными нулю.
Для установки дескрипторов в LDT и их динамического изменения, обращаться к ней через дескриптор LDT нельзя. Дескриптор LDT - это системный объект, он предназначен для автоматического использования процессором (в этом и состоит отличие системных объектов от дескрипторов сегментов; мы уже рассмативали системные объекты - это были шлюзы, из которых строится дескрипторная таблица прерываний IDT). Поэтому для работы с LDT любой задачи нужно определять алиасы (от англ. alias) - в данном случае под алиасом подразумевается сегмент данных, отображённый на те же адреса, по которым располагается LDT задачи.
Точно также для динамического использования GDT нужно определять алиасы - как правило определяют отдельный дескриптор сегмента данных, имеющий такой же адрес и предел, что и GDT.
В мультизадачной системе подразумевается наличие нескольких задач и, следовательно, нескольких LDT, поэтому удобно эти LDT размещать последовательно в памяти, чтобы их можно было описать одним алиасным дескриптором данных. В наших примерах мы будем определять отдельный дескриптор сегмента данных LDT_area, в котором мы будет размещать таблицы LDT всех задач. При конструировании самих дескрипторов LDT мы будем вычислять их адреса, используя базовый адрес сегмента LDT_area.
Использование LDT в задаче не является обязательным. Если все задачи работают на нулевом уровне привилегий, то нет необходимости определять для них отдельные дескрипторы в LDT - достаточно использования GDT. Если задача с PL=0 является сама по себе достаточно сложной, например, менеджер дисковой подсистемы, тогда для удобства программиста можно определить LDT с набором дескрипторов для этой задачи.
Полностью эффективность использования LDT открывается при использовании задач, работающих на не нулевых уровнях. Например, прикладной задаче с PL=3 система может определить LDT с необходимым набором дескрипторов и таким образом изолировать её на третьем уровне привилегий.