Сегментация памяти 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, без каких-либо конкретных применений, определяемых оборудованием. Способ использования сегментных регистров в этих двух режимах различается.[1]

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

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

Три сегмента в реальный режим память (щелкните изображение, чтобы увеличить). Между сегментом 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 бит

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

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

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

Эффективный 20-битный адресное пространство реального режима ограничивает адресуемая память до 220 байт, или 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 линейного адресного пространства. Использование этой функции программистами привело к Ворота A20 проблемы совместимости в более поздних поколениях ЦП, где линейное адресное пространство было расширено до 20 бит.

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

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

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

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

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

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

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

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

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

Логический адрес состоит из 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-битную базу сегмента, указанную в дескрипторе, создавая линейный физический адрес.

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

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

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

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

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

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

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

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

В x64 процессор включается в реальный режим и неотличим от 32-битного Pentium 4. 64-битные инструкции не могут использоваться, если не установлен длинный режим. Когда работает длинный режим, 16-битные инструкции и виртуальный режим x86 отключаются, а защищенный режим исчезает.

GS / FS также используются в gcc с локальное хранилище потока и канарейка протектор стека.

Практики

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

movl $ 42,% fs: (% eax); Эквивалент M [fs: eax] <- 42) в RTL

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

mov dword [фс:eax], 42

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

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

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

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

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

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

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

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

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

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

  1. ^ а б «Руководство разработчика программного обеспечения для архитектур Intel 64 и IA-32», том 3, «Руководство по системному программированию», опубликовано в 2011 г., страница «Том 3A 3-11», в книге написано: «Каждый сегментный регистр имеет «видимую» часть и «скрытую» часть. (Скрытая часть иногда называется «кеш-памятью дескриптора» или «теневым регистром».) Когда селектор сегмента загружается в видимую часть сегментного регистра, процессор также загружает скрытую часть сегментного регистра с помощью базовый адрес, ограничение сегмента и информация управления доступом из дескриптора сегмента, на который указывает селектор сегмента. Информация, кэшированная в сегментном регистре (видимая и скрытая), позволяет процессору транслировать адреса без дополнительных циклов шины для чтения базового адреса и ограничения из дескриптора сегмента."
  2. ^ Корпорация Intel (2004 г.). IA-32 Руководство разработчика программного обеспечения для архитектуры Intel Том 1: Базовая архитектура (PDF).
  3. ^ http://blogs.msdn.com/b/larryosterman/archive/2005/02/08/369243.aspx
  4. ^ POP CS должен использоваться с особой осторожностью и имеет ограниченную полезность, потому что он немедленно изменяет эффективный адрес, который будет вычисляться из указателя инструкции для выборки следующей инструкции. Как правило, гораздо полезнее прыгать вдаль. Наличие POP CS это, вероятно, случайность, поскольку она следует шаблону кодов операций инструкций PUSH и POP для четырех сегментных регистров на 8086 и 8088.

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

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