Таблица глобальных дескрипторов ( GDT ) — это структура данных, используемая процессорами семейства Intel x86, начиная с 80286, для определения характеристик различных областей памяти, используемых во время выполнения программы, включая базовый адрес, размер и привилегии доступа, такие как исполняемость и записываемость. Эти области памяти называются сегментами в терминологии Intel.
GDT — это таблица 8-байтовых записей. Каждая запись может ссылаться на дескриптор сегмента , сегмент состояния задачи (TSS), локальную таблицу дескрипторов (LDT) или шлюз вызова . Шлюзы вызова были разработаны для передачи управления между уровнями привилегий x86, хотя этот механизм не используется в большинстве современных операционных систем.
Также есть таблица локальных дескрипторов (LDT). В GDT можно определить несколько LDT, но только один из них является текущим в любой момент времени: обычно он связан с текущей задачей. В то время как LDT содержит сегменты памяти, которые являются частными для определенного процесса, GDT содержит глобальные сегменты. Процессоры x86 имеют средства для автоматического переключения текущего LDT при определенных машинных событиях, но не имеют средств для автоматического переключения GDT.
Каждый доступ к памяти, выполняемый процессом, всегда проходит через сегмент. На процессоре 80386 и более поздних моделях из-за 32-битных смещений сегментов и ограничений можно сделать так, чтобы сегменты охватывали всю адресуемую память, что делает адресацию относительно сегмента прозрачной для пользователя.
Чтобы сослаться на сегмент, программа должна использовать свой индекс внутри GDT или LDT. Такой индекс называется селектором сегмента (или селектором). Селектор должен быть загружен в регистр сегмента для использования. Помимо машинных инструкций, которые позволяют устанавливать/получать положение GDT и таблицы дескрипторов прерываний (IDT) в памяти, каждая машинная инструкция, ссылающаяся на память, имеет неявный регистр сегмента, иногда два. В большинстве случаев этот регистр сегмента можно переопределить, добавив префикс сегмента перед инструкцией.
Загрузка селектора в сегментный регистр считывает запись GDT или LDT во время загрузки и кэширует свойства сегмента в скрытом регистре. Последующие изменения в GDT или LDT не вступят в силу, пока сегментный регистр не будет перезагружен.
GDT все еще присутствует в 64-битном режиме; GDT должен быть определен, но, как правило, никогда не изменяется и не используется для сегментации. Размер регистра был расширен с 48 до 80 бит, а 64-битные дескрипторы всегда являются «плоскими» (таким образом, от 0x00000000000000000 до 0xFFFFFFFFFFFFFFFFFF). Однако база FS и GS не ограничена 0, и они продолжают использоваться как указатели на смещение элементов, таких как блок среды процесса и блок информации о потоке.
Если системный бит (4-й бит поля Access) очищен, размер дескриптора составляет 16 байт вместо 8. Это связано с тем, что, хотя сегменты кода/данных игнорируются, TSS — нет, но указатель TSS может иметь длину 64 бита, и поэтому дескриптору требуется больше места для вставки старшего dword указателя TSS.
64-разрядные версии Windows запрещают перехват GDT; попытка сделать это приведет к ошибке проверки машины . [1]
Локальная таблица дескрипторов ( LDT ) — это таблица памяти, используемая в архитектуре x86 в защищенном режиме и содержащая дескрипторы сегментов памяти , такие же, как GDT: адрес начала в линейной памяти, размер, исполняемость, возможность записи, привилегии доступа, фактическое присутствие в памяти и т. д.
LDT являются братьями и сестрами Global Descriptor Table (GDT), и каждый определяет до 8192 сегментов памяти, доступных программам - обратите внимание, что в отличие от GDT, запись zeroeth является допустимой записью и может использоваться как любая другая запись LDT. Также обратите внимание, что в отличие от GDT, LDT не может использоваться для хранения определенных системных записей: TSS или LDT. Однако Call Gates и Task Gates подходят.
На процессорах x86, не имеющих функций страничного обмена, таких как Intel 80286 , LDT необходим для реализации отдельных адресных пространств для нескольких процессов. Обычно будет один LDT на пользовательский процесс, описывающий частную память, в то время как общая память и память ядра будут описываться GDT. Операционная система переключит текущий LDT при планировании нового процесса, используя машинную инструкцию LLDT или при использовании TSS . Напротив, GDT, как правило, не переключается (хотя это может произойти, если на компьютере запущены мониторы виртуальных машин, такие как VMware ).
Отсутствие симметрии между обеими таблицами подчеркивается тем фактом, что текущая LDT может автоматически переключаться при определенных событиях, в частности, если используется многозадачность на основе TSS , тогда как для GDT это невозможно. LDT также не может хранить определенные привилегированные типы сегментов памяти (например, TSSes). Наконец, LDT фактически определяется дескриптором внутри GDT, тогда как GDT напрямую определяется линейным адресом.
Создание общей памяти через GDT имеет некоторые недостатки. В частности, такая память видна каждому процессу и имеет равные права. Чтобы ограничить видимость и дифференцировать защиту общей памяти, например, разрешить только чтение для некоторых процессов, можно использовать отдельные записи LDT, указывающие на те же области физической памяти и созданные только в LDT процессов, которые запросили доступ к данной области общей памяти.
Записи LDT (и GDT), которые указывают на идентичные области памяти, называются псевдонимами . Псевдонимы также обычно создаются для получения доступа на запись к сегментам кода: исполняемый селектор не может использоваться для записи. (Программы защищенного режима, созданные в так называемой крошечной модели памяти , где все находится в одном сегменте памяти, должны использовать отдельные селекторы для кода и данных/стека, что делает оба селектора технически «псевдонимами».) В случае GDT псевдонимы также создаются для получения доступа к системным сегментам, таким как TSS.
Сегменты имеют флаг «Present» в своих дескрипторах, что позволяет удалять их из памяти, если возникает такая необходимость. Например, сегменты кода или немодифицированные сегменты данных могут быть отброшены, а измененные сегменты данных могут быть выгружены на диск. Однако, поскольку целые сегменты должны работать как единое целое, необходимо ограничить их размер, чтобы гарантировать, что подкачка может происходить своевременно. Однако использование меньших, более легко заменяемых сегментов означает, что регистры сегментов должны перезагружаться чаще, что само по себе является трудоемкой операцией.
Микропроцессор Intel 80386 ввел страничную организацию — выделение отдельных страниц физической памяти (которые сами по себе являются очень маленькими единицами памяти) по тем же виртуальным адресам, с тем преимуществом, что страничная организация диска намного быстрее и эффективнее, чем подкачка сегментов. Поэтому современные 32-разрядные операционные системы x86 используют LDT очень мало, в основном для запуска устаревшего 16-разрядного кода.
Если 16-битный код должен работать в 32-битной среде при разделении памяти (это происходит, например, при запуске программ OS/2 1.x на OS/2 2.0 и более поздних версиях), LDT должен быть написан таким образом, чтобы каждый плоский (выделенный на страницу) адрес также имел селектор в LDT (обычно это приводит к заполнению LDT записями по 64 КБ). Этот метод иногда называют тайлингом LDT . Ограниченный размер LDT означает, что виртуальное плоское адресное пространство должно быть ограничено 512 мегабайтами (8191 раз по 64 КБ) — именно это и происходит в OS/2, хотя это ограничение было исправлено в версии 4.5. Также необходимо убедиться, что объекты, размещенные в 32-битной среде, не пересекают границы 64 КБ; это приводит к некоторой трате адресного пространства.
Если 32-битному коду не нужно передавать произвольные объекты памяти 16-битному коду, например, предположительно в эмуляции OS/2 1.x, присутствующей в Windows NT или в слое эмуляции Windows 3.1 , то нет необходимости искусственно ограничивать размер 32-битного адресного пространства.
операционная система обнаружит одну из этих модификаций или любой другой несанкционированный патч, она сгенерирует проверку на наличие ошибок и завершит работу системы.