сегментация памяти x86 - x86 memory segmentation

Сегментация памяти x86 относится к реализации сегментации памяти в архитектуре набора команд компьютера Intel x86 . Сегментация была введена в Intel 8086 в 1978 году как способ позволить программам обращаться к более чем 64 КБ (65 536  байт ) памяти. Intel 80286 представила вторую версию сегментации в 1982 году , которая была добавлена поддержка виртуальной памяти и защиты памяти . На этом этапе исходная модель была переименована в реальный режим , а новая версия получила название защищенного режима . Архитектура x86-64 , представленная в 2003 году, в значительной степени отказалась от поддержки сегментации в 64-битном режиме.

Как в реальном, так и в защищенном режиме система использует 16-битные сегментные регистры для получения фактического адреса памяти. В реальном режиме регистры CS, DS, SS и ES указывают на текущий используемый сегмент программного кода (CS), текущий сегмент данных (DS), текущий сегмент стека (SS) и один дополнительный сегмент, определенный программистом. (ES). Intel 80386 , введенный в 1985 году, добавляет два дополнительных сегментных регистров, FS и GS, без каких - либо конкретных целей , определенных аппаратных средств. Способ использования сегментных регистров в этих двух режимах различается.

Выбор сегмента обычно выполняется процессором по умолчанию в соответствии с выполняемой функцией. Инструкции всегда берутся из сегмента кода. Любое нажатие или извлечение стека или любая ссылка на данные, относящиеся к стеку, используют сегмент стека. Все другие ссылки на данные используют сегмент данных. Дополнительный сегмент является местом назначения по умолчанию для строковых операций (например, MOVS или CMPS). FS и GS не имеют назначенного аппаратного обеспечения. Формат инструкции позволяет использовать необязательный байт префикса сегмента, который при желании может использоваться для переопределения сегмента по умолчанию для выбранных инструкций.

Реальный режим

Три сегмента в памяти реального режима (щелкните изображение, чтобы увеличить). Между сегментом 2 и сегментом 3 имеется перекрытие; байты в бирюзовой области могут использоваться из обоих селекторов сегментов.

В реальном режиме или режиме V86 размер сегмента может варьироваться от 1 байта до 65 536 байтов (с использованием 16-битных смещений).

Селектор 16-битного сегмента в сегментном регистре интерпретируется как 16 старших разрядов линейного 20-битного адреса, называемого адресом сегмента, из которых все остальные четыре младших бита являются нулями. Адрес сегмента всегда добавляется к 16-битному смещению в инструкции, чтобы получить линейный адрес, который совпадает с физическим адресом в этом режиме. Например, сегментированный адрес 06EFh: 1234h (здесь суффикс «h» означает шестнадцатеричный ) имеет селектор сегмента 06EFh, представляющий адрес сегмента 06EF0h, к которому добавляется смещение, давая линейный адрес 06EF0h + 1234h = 08124h.

  0000 0110 1110 1111  0000 Сегмент , 16 бит, сдвинутый на 4 бита влево (или умноженный на 0x10)
+      0001 0010 0011 0100 Смещение , 16 бит
                          
  0000 1000 0001 0010 0100 Адрес , 20 бит

Из-за способа добавления адреса сегмента и смещения один линейный адрес может быть сопоставлен максимум с 2 12 = 4096 отдельными парами сегмент: смещение. Например, линейный адрес 08124h может иметь сегментированные адреса 06EFh: 1234h, 0812h: 0004h, 0000h: 8124h и т. Д.

Это может сбивать с толку программистов, привыкших к уникальным схемам адресации, но это также может быть полезно, например, при адресации нескольких вложенных структур данных. Хотя сегменты реального режима всегда имеют длину 64  КБ , практический эффект заключается только в том, что ни один сегмент не может быть длиннее 64 КБ, а не в том, что каждый сегмент должен быть длиной 64 КБ. Поскольку в реальном режиме нет защиты или ограничения привилегий, даже если бы сегмент мог быть определен как меньше 64 КБ, программа все равно могла бы координировать и удерживать границы своих сегментов, поскольку любая программа может всегда обращаться к любой памяти (поскольку он может произвольно устанавливать селекторы сегментов для изменения адресов сегментов без всякого контроля). Следовательно, реальный режим можно представить как имеющий переменную длину для каждого сегмента в диапазоне от 1 до 65 536 байтов, что просто не поддерживается ЦП.

(Начальные нули линейного адреса, сегментированных адресов, а также полей сегмента и смещения показаны здесь для ясности. Обычно они опускаются.)

Эффективное 20-битное адресное пространство реального режима ограничивает адресуемую память до 2 20  байтов, или 1 048 576 байтов (1  МБ ). Это напрямую связано с конструкцией аппаратного обеспечения Intel 8086 (и, впоследствии, тесно связанного с ним 8088), у которого было ровно 20 адресных контактов . (Оба были упакованы в 40-контактные DIP-пакеты; даже при наличии всего 20 адресных линий шины адреса и данных были мультиплексированы, чтобы вместить все линии адреса и данных в пределах ограниченного количества контактов.)

Каждый сегмент начинается с числа, кратного 16 байтам, называемого абзацем , от начала линейного (плоского) адресного пространства. То есть с интервалом в 16 байт. Поскольку все сегменты имеют длину 64 КБ, это объясняет, как может происходить перекрытие между сегментами и почему к любому месту в линейном адресном пространстве памяти можно получить доступ с помощью многих пар сегмент: смещение. Фактическое местоположение начала сегмента в линейном адресном пространстве может быть вычислено с помощью сегмента × 16. Значение сегмента 0Ch (12) даст линейный адрес в C0h (192) в линейном адресном пространстве. Затем к этому числу можно добавить смещение адреса. 0Ch: 0Fh (12:15) будет C0h + 0Fh = CFh (192 + 15 = 207), CFh (207) будет линейным адресом. Такие трансляции адресов выполняются блоком сегментации ЦП. Последний сегмент, FFFFh (65535), начинается с линейного адреса FFFF0h (1048560), за 16 байтов до конца 20-битного адресного пространства, и, таким образом, может иметь доступ со смещением до 65 536 байтов до 65 520 (65536). −16) байтов после конца 20-битного адресного пространства 8088. На 8088 эти обращения к адресам были перенесены в начало адресного пространства, так что 65535: 16 будет обращаться к адресу 0, а 65533: 1000 будет обращаться к адресу 952 линейного адресного пространства. Использование этой функции программистами привело к проблемам совместимости Gate A20 в более поздних поколениях ЦП, где линейное адресное пространство было расширено до 20 бит.

В 16-битном реальном режиме позволить приложениям использовать несколько сегментов памяти (чтобы получить доступ к большему объему памяти, чем доступно в любом одном 64-килобайтном сегменте) довольно сложно, но рассматривалось как необходимое зло для всех, кроме самых маленьких инструментов ( что можно было бы сделать с меньшим объемом памяти). Корень проблемы в том, что отсутствуют подходящие адресно-арифметические инструкции, подходящие для плоской адресации всего диапазона памяти. Плоская адресация возможна путем применения нескольких инструкций, что, однако, приводит к более медленным программам.

Концепция модели памяти основана на настройке сегментных регистров. Например, в крошечной модели CS = DS = SS код программы, данные и стек содержатся в одном сегменте размером 64 КБ. В небольшой модели памяти DS = SS, поэтому и данные, и стек находятся в одном сегменте; CS указывает на другой сегмент кода размером до 64 КБ.

Защищенный режим

Три сегмента в памяти защищенного режима (щелкните изображение, чтобы увеличить), с локальной таблицей дескрипторов .

80286 защищенный режим

80286 «ы защищенный режим расширяет адресное пространство процессора до 2 24 байт (16 мегабайт), но не путем корректировки значения сдвига. Вместо этого 16-битные сегментные регистры теперь содержат индекс в таблице дескрипторов сегментов, содержащей 24-битные базовые адреса, к которым добавляется смещение. Для поддержки старого программного обеспечения процессор запускается в «реальном режиме» - режиме, в котором он использует модель сегментированной адресации 8086. Однако есть небольшая разница: полученный физический адрес больше не усекается до 20 бит, поэтому реально указатели режима (но не указатели 8086) теперь могут ссылаться на адреса от 100000 16 до 10FFEF 16 . Эта область памяти размером примерно 64 килобайта была известна как область высокой памяти (HMA), и более поздние версии DOS могли использовать ее для увеличения доступной «обычной» памяти (то есть в пределах первого МБ ). С добавлением HMA общее адресное пространство составляет примерно 1,06 МБ. Хотя 80286 не усекает адреса реального режима до 20 бит, система, содержащая 80286, может делать это с аппаратным обеспечением, внешним по отношению к процессору, путем стробирования 21-й адресной строки, линии A20 . IBM PC AT предоставил оборудование для этого (для полной обратной совместимости с программным обеспечением для исходных моделей IBM PC и PC / XT ), как и все последующие клоны ПК « AT- класса».

Защищенный режим 286 использовался редко, поскольку он исключил бы большое количество пользователей с машинами 8086/88. Более того, по-прежнему требовалось делить память на сегменты по 64К, как это делалось в реальном режиме. Это ограничение можно обойти на 32-битных процессорах, которые позволяют использовать указатели памяти размером более 64 КБ, однако, поскольку поле ограничения сегмента имеет длину всего 24 бита, максимальный размер сегмента, который может быть создан, составляет 16 МБ (хотя подкачка может использоваться для выделения большего количества памяти, ни один отдельный сегмент не может превышать 16 МБ). Этот метод обычно использовался в приложениях Windows 3.x для создания плоского пространства памяти, хотя, поскольку сама ОС все еще была 16-битной, вызовы API не могли выполняться с 32-битными инструкциями. Таким образом, по-прежнему необходимо было разместить весь код, выполняющий вызовы API, в сегментах по 64k.

После активации защищенного режима 286 из него нельзя было выйти, кроме как путем аппаратного сброса. Машины, следующие за растущим стандартом IBM PC / AT, могли симулировать перезагрузку ЦП через стандартизированный контроллер клавиатуры, но это было значительно медленнее. Windows 3.x обошла обе эти проблемы, намеренно вызвав тройную ошибку в механизмах обработки прерываний ЦП, которая почти мгновенно заставила ЦП вернуться в реальный режим.

Подробный рабочий процесс блока сегментации

Логический адрес состоит из 16-битного селектора сегмента (обеспечивающего 13 + 1 бит адреса) и 16-битного смещения. Селектор сегмента должен находиться в одном из сегментных регистров. Этот селектор состоит из 2-битного запрошенного уровня привилегий (RPL), 1-битного индикатора таблицы (TI) и 13-битного индекса.

При попытке трансляции адреса данного логического адреса процессор считывает структуру дескриптора 64-битного сегмента либо из глобальной таблицы дескрипторов, когда TI = 0, либо из таблицы локальных дескрипторов, когда TI = 1. Затем он выполняет проверку привилегий:

макс (CPL, RPL) ≤ DPL

где CPL - это текущий уровень привилегий (находится в младших 2 битах регистра CS), RPL - это запрошенный уровень привилегий от селектора сегмента, а DPL - это уровень привилегий дескриптора сегмента (находится в дескрипторе). Все уровни привилегий представляют собой целые числа в диапазоне 0–3, где наименьшее число соответствует высшей привилегии.

Если неравенство ложно, процессор генерирует ошибку общей защиты (GP) . В противном случае преобразование адресов продолжается. Затем процессор берет 32-битное или 16-битное смещение и сравнивает его с пределом сегмента, указанным в дескрипторе сегмента. Если он больше, генерируется ошибка GP. В противном случае процессор добавляет к смещению 24-битную базу сегмента, указанную в дескрипторе, создавая линейный физический адрес.

Проверка привилегий выполняется только тогда, когда сегментный регистр загружен, потому что дескрипторы сегментов кэшируются в скрытых частях сегментных регистров.

80386 защищенный режим

В Intel 80386 и более поздних версиях защищенный режим сохраняет механизм сегментации защищенного режима 80286, но добавлен модуль поискового вызова в качестве второго уровня трансляции адресов между модулем сегментации и физической шиной. Кроме того, что важно, смещения адреса являются 32-битными (вместо 16-битных), и база сегмента в каждом дескрипторе сегмента также 32-битная (вместо 24-битной). В остальном общая работа блока сегментации не меняется. Блок поискового вызова может быть включен или отключен; если он отключен, операция такая же, как на 80286. Если модуль пейджинга включен, адреса в сегменте теперь являются виртуальными адресами, а не физическими адресами, как на 80286. То есть начальный адрес сегмента, смещение, и последний 32-битный адрес, полученный блоком сегментации путем сложения этих двух адресов, являются виртуальными (или логическими) адресами, когда блок пейджинговой связи включен. Когда блок сегментации генерирует и проверяет эти 32-битные виртуальные адреса, включенный блок пейджинга наконец преобразует эти виртуальные адреса в физические адреса. Физические адреса на 386 32-битные , но могут быть больше на новых процессорах, которые поддерживают расширение физического адреса .

80386 также представил два новых регистра сегмента данных общего назначения, FS и GS, к исходному набору из четырех регистров сегментов (CS, DS, ES и SS).

ЦП 386 можно вернуть в реальный режим, очистив бит в регистре управления CR0, однако это привилегированная операция для обеспечения безопасности и надежности. Для сравнения, 286 можно было вернуть в реальный режим только путем принудительного сброса процессора, например, из-за тройной неисправности или использования внешнего оборудования.

Более поздние разработки

Архитектура x86-64 не использует сегментацию в длинном режиме (64-битный режим). Четыре из сегментных регистров, CS, SS, DS и ES, принудительно устанавливаются на 0, а предел - на 2 64 . Сегментные регистры FS и GS могут иметь ненулевой базовый адрес. Это позволяет операционным системам использовать эти сегменты для специальных целей. В отличие от механизма таблицы глобальных дескрипторов, используемого в устаревших режимах, базовый адрес этих сегментов хранится в регистре, зависящем от модели . Архитектура x86-64 дополнительно предусматривает специальную SWAPGS инструкцию, которая позволяет поменять местами режима ядра и пользовательского режима адреса базы.

Например, Microsoft Windows на x86-64 использует сегмент GS для указания на блок среды потока , небольшую структуру данных для каждого потока , которая содержит информацию об обработке исключений, локальных переменных потока и другом состоянии каждого потока. Точно так же ядро Linux использует сегмент GS для хранения данных по процессорам.

GS / FS также используется в GCC «ы хранения нити локальной и канареечно на основе стека протектор.

Практики

Логические адреса могут быть явно указаны на языке ассемблера x86 , например (синтаксис AT&T):

movl $42, %fs:(%eax)  ; Equivalent to M[fs:eax]<-42) in RTL

или в синтаксисе Intel :

mov dword [fs:eax], 42

Однако сегментные регистры обычно используются неявно.

  • Все инструкции ЦП неявно выбираются из сегмента кода, указанного селектором сегмента, хранящимся в регистре CS.
  • Большинство обращений к памяти поступают из сегмента данных, указанного селектором сегмента, хранящимся в регистре DS. Они также могут поступать из дополнительного сегмента, указанного селектором сегментов, хранящимся в регистре ES, если префикс переопределения сегмента предшествует инструкции, которая делает ссылку на память. Большинство, но не все, инструкции, которые используют DS по умолчанию, принимают префикс отмены ES.
  • Ссылки на стек процессора , либо неявно (например, инструкции push и pop ) или явно ( доступ к памяти с использованием регистров (E) SP или (E) BP ), используют сегмент стека, указанный селектором сегмента, содержащимся в регистре SS.
  • Строковые инструкции (например, stos , movs ) вместе с сегментом данных также используют дополнительный сегмент, указанный селектором сегментов, хранящимся в регистре ES.

Сегментация не может быть отключена на процессорах x86-32 (это верно и для 64-битного режима, но выходит за рамки обсуждения), поэтому многие 32-битные операционные системы имитируют плоскую модель памяти , устанавливая базы всех сегментов в 0 чтобы сделать сегментацию нейтральной по отношению к программам. Например, ядро Linux устанавливает только 4 сегмента общего назначения:

Имя Описание База Предел DPL
__KERNEL_CS Сегмент кода ядра 0 4 ГиБ 0
__KERNEL_DS Сегмент данных ядра 0 4 ГиБ 0
__USER_CS Сегмент кода пользователя 0 4 ГиБ 3
__USER_DS Сегмент пользовательских данных 0 4 ГиБ 3

Поскольку во всех случаях для базы установлено значение 0, а для ограничения - 4 ГиБ, модуль сегментации не влияет на адресацию программных проблем до того, как они поступят в модуль подкачки . (Это, конечно, относится к процессорам 80386 и более поздним версиям, поскольку более ранние процессоры x86 не имеют модуля подкачки.)

Текущая версия Linux также использует GS для указания на локальное хранилище потока .

Сегменты могут быть определены как сегменты кода, данных или системы. Дополнительные биты разрешений позволяют сделать сегменты только для чтения, чтения / записи, выполнения и т. Д.

В защищенном режиме код всегда может изменять все сегментные регистры, кроме CS ( селектора сегментов кода ). Это связано с тем, что текущий уровень привилегий (CPL) процессора хранится в младших 2 битах регистра CS. Единственный способ поднять уровень привилегий процессора (и перезагрузить CS) - использовать инструкции lcall (дальний вызов) и int (прерывание) . Точно так же единственный способ понизить уровень привилегий (и перезагрузить CS) - это использовать инструкции lret (возврат в дальний угол ) и iret (возврат из прерывания). В реальном режиме код также может изменять регистр CS, совершая дальний переход (или используя недокументированную POP CSинструкцию на 8086 или 8088)). Конечно, в реальном режиме уровней привилегий нет; все программы имеют абсолютный неограниченный доступ ко всей памяти и всем инструкциям ЦП.

Дополнительные сведения о сегментации см. В руководствах по IA-32, которые находятся в свободном доступе на веб-сайтах AMD или Intel .

Примечания и ссылки

Смотрите также

внешние ссылки