stringtranslate.com

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

Сегментация памяти 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]

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

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

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

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

Благодаря способу добавления адреса сегмента и смещения один линейный адрес может быть сопоставлен до 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 (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 КБ.

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

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

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 КБ, однако, поскольку поле 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) ; Эквивалент M[fs:eax]<-42) в RTL

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

mov dword [ fs : eax ], 42   

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

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

Поскольку база во всех случаях установлена ​​на 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 .

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

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

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

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