сегментация памяти x86
Эта статья нуждается в дополнительных цитатах для проверки . ( июнь 2017 г. ) |
Сегментация памяти x86 относится к реализации сегментации памяти в Intel x86 компьютера архитектуре набора команд . Сегментация была введена в процессоре Intel 8086 в 1978 году как способ разрешить программам адресовать более 64 КБ (65 536 байт ) памяти. В 1982 году Intel 80286 представил вторую версию сегментации, в которой добавлена поддержка виртуальной памяти и защита памяти . На этом этапе исходный режим был переименован в реальный режим , а новая версия получила название защищенного режима . Архитектура x86-64 , представленная в 2003 году, в значительной степени отказалась от поддержки сегментации в 64-битном режиме.
И в реальном, и в защищенном режимах система использует 16-битные сегментные регистры для получения фактического адреса памяти. В реальном режиме регистры CS, DS, SS и ES указывают на используемый в данный момент сегмент программного кода (CS), текущий сегмент данных (DS), текущий сегмент стека (SS) и один дополнительный сегмент, определяемый программистом. (ЭС). Intel 80386 , представленный в 1985 году, добавляет два дополнительных сегментных регистра, FS и GS, без какого-либо конкретного использования, определяемого аппаратным обеспечением. Способ использования сегментных регистров в этих двух режимах различается. [ 1 ]
Выбор сегмента обычно производится процессором по умолчанию в соответствии с выполняемой функцией. Инструкции всегда извлекаются из сегмента кода. Любое нажатие или извлечение стека, а также любая ссылка на данные, ссылающаяся на стек, используют сегмент стека. Все остальные ссылки на данные используют сегмент данных. Дополнительный сегмент является местом назначения по умолчанию для строковых операций (например, MOVS или CMPS). FS и GS не имеют аппаратно назначенного использования. Формат инструкции допускает дополнительный байт префикса сегмента , который при желании можно использовать для замены сегмента по умолчанию для выбранных инструкций. [ 2 ]
Реальный режим
[ редактировать ]В реальном режиме или режиме 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 бит |
Из-за способа добавления адреса сегмента и смещения один линейный адрес может быть сопоставлен до двух 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 (65 536 байт). −16) байт после конца 20-битного адресного пространства 8088. На 8088 эти обращения к адресам были перенесены в начало адресного пространства, так что 65535:16 будет обращаться к адресу 0, а 65533:1000 будет обращаться к адресу 952 линейного адресного пространства. Использование этой возможности программистами привело к Проблемы совместимости Gate A20 в более поздних поколениях ЦП, где линейное адресное пространство было расширено до 20 бит.
В реальном 16-битном режиме разрешить приложениям использовать несколько сегментов памяти (чтобы получить доступ к большему объему памяти, чем доступно в любом одном 64-килобайтном сегменте) довольно сложно, но это рассматривалось как неизбежное зло для всех инструментов, кроме самых маленьких ( что можно было бы сделать с меньшим объемом памяти). Корень проблемы заключается в том, что не существует подходящих инструкций адресной арифметики, подходящих для плоской адресации всего диапазона памяти. [ нужна ссылка ] Плоская адресация возможна за счет применения нескольких инструкций, что, однако, приводит к замедлению работы программ.
Концепция модели памяти вытекает из настройки сегментных регистров. Например, в крошечной модели CS=DS=SS, то есть код программы, данные и стек содержатся в одном сегменте размером 64 КБ. В модели малой памяти DS=SS, поэтому и данные, и стек находятся в одном сегменте; CS указывает на другой сегмент кода размером до 64 КБ.
Защищенный режим
[ редактировать ]Этот раздел нуждается в дополнительных цитатах для проверки . ( Август 2015 г. ) |
80286 защищенный режим
[ редактировать ]режим 80286 Защищенный 2 расширяет адресное пространство процессора до 24 байт (16 мегабайт), а не путем регулировки значения сдвига. Вместо этого 16-битные регистры сегментов теперь содержат индекс в таблицу дескрипторов сегментов, содержащую 24-битные базовые адреса, к которым добавляется смещение. Для поддержки старого программного обеспечения процессор запускается в «реальном режиме», режиме, в котором он использует модель сегментированной адресации 8086. Однако есть небольшая разница: результирующий физический адрес больше не усекается до 20 бит, поэтому он становится реальным. Указатели режима (но не указатели 8086) теперь могут ссылаться на адреса между 100000 16 и 10FFEF 16 . Эта область памяти размером примерно 64 килобайта была известна как область высокой памяти (HMA), и более поздние версии DOS могли использовать ее для увеличения доступной «обычной» памяти (т. е. в пределах первого МБ ). С добавлением HMA общий адресное пространство составит примерно 1,06 МБ. Хотя 80286 не усекает адреса реального режима до 20 бит, система, содержащая 80286, может делать это с помощью аппаратного обеспечения, внешнего по отношению к процессору, путем исключения 21-й адресной строки, Линия А20 . IBM PC AT предоставил аппаратное обеспечение для этого (для полной обратной совместимости с программным обеспечением исходных моделей IBM PC и PC/XT ), как и все последующие « AT клоны ПК -класса».
Защищенный режим 286 использовался редко, поскольку он исключал бы большое количество пользователей с машинами 8086/88. Более того, по-прежнему требовалось делить память на сегменты по 64 КБ, как это делалось в реальном режиме. Это ограничение можно обойти на 32-битных процессорах, которые позволяют использовать указатели памяти размером более 64 КБ, однако, поскольку поле Segment Limit имеет длину всего 24 бита, максимальный размер сегмента, который может быть создан, составляет 16 МБ (хотя подкачка может использоваться для выделения дополнительной памяти, размер отдельного сегмента не может превышать 16 МБ). Этот метод обычно использовался в приложениях Windows 3.x для создания плоского пространства памяти, хотя, поскольку сама ОС все еще была 16-битной, вызовы API нельзя было выполнять с помощью 32-битных инструкций. Таким образом, весь код, выполняющий вызовы API, по-прежнему необходимо было разместить в сегментах по 64 тыс.
После вызова защищенного режима 286 из него невозможно выйти, кроме как путем выполнения аппаратного сброса. Машины, соответствующие развивающемуся стандарту IBM PC/AT, могли симулировать перезагрузку ЦП через стандартизированный контроллер клавиатуры, но это происходило значительно медленно. Windows 3.x обошла обе эти проблемы, намеренно вызывая тройную ошибку в механизмах обработки прерываний ЦП, из-за которой ЦП почти мгновенно возвращался в реальный режим. [ 3 ]
Подробный рабочий процесс блока сегментации
[ редактировать ]Логический адрес состоит из 16-битного селектора сегмента (обеспечивающего 13+1 бит адреса) и 16-битного смещения. Селектор сегмента должен находиться в одном из регистров сегмента. Этот селектор состоит из 2-битного запрашиваемого уровня привилегий (RPL), 1-битного индикатора таблицы (TI) и 13-битного индекса.
При попытке трансляции данного логического адреса процессор считывает 64-битную структуру дескриптора сегмента либо из глобальной таблицы дескрипторов , когда TI=0, либо из локальной таблицы дескрипторов , когда TI=1. Затем он выполняет проверку привилегий:
- макс(CPL, RPL) ≤ DPL
где CPL — текущий уровень привилегий (находится в младших двух битах регистра CS), RPL — запрошенный уровень привилегий от селектора сегмента, а DPL — уровень привилегий дескриптора сегмента (находится в дескрипторе). Все уровни привилегий представляют собой целые числа в диапазоне 0–3, где наименьшее число соответствует наивысшему привилегию.
Если неравенство ложно, процессор генерирует ошибку общей защиты (GP) . В противном случае трансляция адресов продолжается. Затем процессор берет 32-битное или 16-битное смещение и сравнивает его с пределом сегмента, указанным в дескрипторе сегмента. Если оно больше, генерируется ошибка GP. В противном случае процессор добавляет к смещению 24-битную базу сегмента, указанную в дескрипторе, создавая линейный физический адрес.
Проверка привилегий выполняется только при загрузке регистра сегмента, поскольку дескрипторы сегментов кэшируются в скрытых частях регистров сегмента. [ нужна ссылка ] [ 1 ]
80386 защищенный режим
[ редактировать ]В Intel 80386 и более поздних версиях защищенный режим сохраняет механизм сегментации защищенного режима 80286, но подкачки в качестве второго уровня трансляции адресов между модулем сегментации и физической шиной был добавлен блок . Также, что немаловажно, смещения адресов 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 дополнительно предоставляет специальную инструкцию 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 сегмента общего назначения:
Имя | Описание | База | Лимит | ДПЛ |
---|---|---|---|---|
__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) процессора хранится в младших двух битах регистра CS. Единственный способ повысить уровень привилегий процессора (и перезагрузить CS) — это использовать инструкции lcall (дальний вызов) и int (прерывание) . Аналогично, единственные способы снизить уровень привилегий (и перезагрузить CS) — это использовать инструкции lret (дальний возврат) и iret (возврат по прерыванию). В реальном режиме код также может изменить регистр CS, совершив дальний переход (или используя недокументированный POP CS
инструкцию на 8086 или 8088). [ 4 ] Конечно, в реальном режиме уровней привилегий нет; все программы имеют абсолютный неконтролируемый доступ ко всей памяти и всем инструкциям ЦП.
Дополнительную информацию о сегментации см. в руководствах IA-32, которые можно бесплатно найти на веб-сайтах AMD или Intel .
Примечания и ссылки
[ редактировать ]- ^ Перейти обратно: а б «Руководство разработчика программного обеспечения для архитектур Intel 64 и IA-32», том 3, «Руководство по системному программированию», опубликовано в 2011 году, на странице «Том 3A 3-11» написано: « Каждый регистр сегмента имеет «видимый» часть и «скрытую» часть (Скрытую часть иногда называют «кэшем дескриптора» или «теневым регистром».) Когда селектор сегмента загружается в видимую часть регистра сегмента, процессор также загружает и «скрытую» часть. скрытая часть регистра сегмента с базовым адресом, пределом сегмента и информацией контроля доступа из дескриптора сегмента, на который указывает селектор сегмента. Информация, кэшированная в регистре сегмента (видимая и скрытая), позволяет процессору транслировать адреса без дополнительных затрат. циклов шины для чтения базового адреса и ограничения из дескриптора сегмента » .
- ^ Корпорация Intel (2004 г.). IA-32 Руководство разработчика программного обеспечения для архитектуры Intel, том 1: Базовая архитектура (PDF) .
- ^ «Блоги разработчиков» .
- ^
POP CS
должен использоваться с особой осторожностью и имеет ограниченную полезность, поскольку он немедленно изменяет эффективный адрес, который будет вычислен из указателя инструкции для получения следующей инструкции. Вообще, дальний прыжок гораздо полезнее. СуществованиеPOP CS
Вероятно, это случайность, поскольку она соответствует шаблону кодов операций инструкций PUSH и POP для четырехсегментных регистров на 8086 и 8088.