Какую структуру имеет дескриптор сегмента оперативной памяти

Обновлено: 07.07.2024

Для адресации операндов в физическом адресном пространстве программы используют логическую адресацию. Процессор автоматически транслирует логические адреса в физические, выдаваемые затем на системную шину.

Архитектура компьютера различает физическое адресное пространство (ФАП) и логическое адресное пространство (ЛАП). Физическое адресное пространство представляет собой простой одномерный массив байтов, доступ к которому реализуется аппаратурой памяти по адресу, присутствующему на шине адреса микропроцессорной системы . Логическое адресное пространство организуется самим программистом исходя из конкретных потребностей. Трансляцию логических адресов в физические осуществляет блок управления памятью MMU .

В архитектуре современных микропроцессоров ЛАП представляется в виде набора элементарных структур: байтов, сегментов и страниц. В микропроцессорах используются следующие варианты организации логического адресного пространства:

  • плоское (линейное) ЛАП: состоит из массива байтов, не имеющего определенной структуры; трансляция адреса не требуется, так как логический адрес совпадает с физическим;
  • сегментированное ЛАП: состоит из сегментов - непрерывных областей памяти, содержащих в общем случае переменное число байтов; логический адрес содержит 2 части: идентификатор сегмента и смещение внутри сегмента; трансляцию адреса проводит блок сегментации MMU ;
  • страничное ЛАП: состоит из страниц - непрерывных областей памяти, каждая из которых содержит фиксированное число байтов. Логический адрес состоит из номера (идентификатора) страницы и смещения внутри страницы; трансляция логического адреса в физический проводится блоком страничного преобразования MMU ;
  • сегментно-страничное ЛАП: состоит из сегментов, которые, в свою очередь, состоят из страниц; логический адрес состоит из идентификатора сегмента и смещения внутри сегмента. Блок сегментного преобразования MMU проводит трансляцию логического адреса в номер страницы и смещение в ней, которые затем транслируются в физический адрес блоком страничного преобразования MMU .

Таким образом, основой получения физического адреса памяти служит логический адрес . В какой-то степени логическое адресное пространство , с которым имеет дело программист, можно сравнить со структурой книги, где аналогом сегмента выступает рассказ, страница книги соответствует странице ЛАП, а искомая информация - это некоторое слово . При этом если память организована как линейная, то номер искомого слова задается в явном виде и просто отсчитывается от начала книги. При сегментном представлении памяти искомое слово определяется его номером в заданном рассказе. Страничное представление памяти предполагает задание информации о слове в виде номера страницы в книге и номера слова на указанной странице. При сегментно-страничном представлении логический адрес слова задается номером слова в определенном рассказе. В этом случае по оглавлению книги определяется номер страницы, с которой начинается указанный рассказ. Затем, зная количество слов на странице и положение слова в рассказе, можно вычислить страницу книги и положение искомого слова на этой странице.

Формирование физического адреса в универсальном микропроцессоре при различных режимах работы

Микропроцессор способен работать в двух режимах: реальном и защищенном.

При работе в реальном режиме возможности процессора ограничены: емкость адресуемой памяти составляет 1 Мбайт, отсутствует страничная организация памяти, сегменты имеют фиксированную длину 2 16 байт .

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

В реальном режиме сегментные регистры процессора содержат старшие 16 бит физического адреса начала сегмента. Сдвинутый на 4 разряда влево селектор дает 20-разрядный базовый адрес сегмента. Физический адрес получается путем сложения этого адреса с 16-разрядным значением смещения в сегменте, формируемого по заданному режиму адресации для операнда или извлекаемому из регистра EIP для команды (рис. 3.1). По полученному адресу происходит выборка информации из памяти.

Схема получения физического адреса

Наиболее полно возможности микропроцессора по адресации памяти реализуются при работе в защищенном режиме. Объем адресуемой памяти увеличивается до 4 Гбайт, появляется возможность страничного режима адресации. Сегменты могут иметь переменную длину от 1 байта до 4 Гбайт.

Общая схема формирования физического адреса микропроцессором , работающим в защищенном режиме, представлена на рис. 3.2.

Как уже отмечалось, основой формирования физического адреса служит логический адрес . Он состоит из двух частей: селектора и смещения в сегменте.

Селектор содержится в сегментном регистре микропроцессора и позволяет найти описание сегмента (дескриптор) в специальной таблице дескрипторов. Дескрипторы сегментов хранятся в специальных системных объектах глобальной ( GDT ) и локальных ( LDT ) таблицах дескрипторов. Дескриптор играет очень важную роль в функционировании микропроцессора , от формирования физического адреса при различной организации адресного пространства и до организации мультипрограммного режима работы. Поэтому рассмотрим его структуру более подробно.

Сегменты микропроцессора , работающего в защищенном режиме, характеризуются большим количеством параметров. Поэтому в универсальных 32-разрядных микропроцессорах информация о сегменте хранится в

Формирование физического адреса при сегментно-страничной организации памяти


Рис. 3.2. Формирование физического адреса при сегментно-страничной организации памяти

Структура дескриптора сегмента представлена на рис. 3.3.

Структура дескриптора сегмента

Мы будем рассматривать именно структуру, а не формат дескриптора, так как при переходе от микропроцессора i286 к 32-разрядному МП расположение отдельных полей дескриптора потеряло свою стройность и частично стало иметь вид "заплаток", поставленных с целью механического увеличения разрядности этих полей.

32-разрядное поле базового адреса позволяет определить начальный адрес сегмента в любой точке адресного пространства в 2 32 байт (4 Гбайт).

Поле предела (limit) указывает длину сегмента (точнее, длину сегмента минус 1: если в этом поле записан 0, то это означает, что сегмент имеет длину 1) в адресуемых единицах, то есть максимальный размер сегмента равен 2 20 элементов.

Величина элемента определяется одним из атрибутов дескриптора битом G ( Granularity - гранулярность , или дробность):

G=\begin</p>
\mbox\\ \mbox \end

Таким образом, сегмент может иметь размер с точностью до 1 байта в диапазоне от 1 байта до 1 Мбайт (при G = 0 ). При объеме страницы в 2 12 = 4 Кбайт можно задать объем сегмента до 4 Гбайт (при G = l ):

V_<\mbox<сегм макс></p>
>=2^_*2^_=2^байт

Так как в архитектуре IA-32 сегмент может начинаться в произвольной точке адресного пространства и иметь произвольную длину, сегменты в памяти могут частично или полностью перекрываться.

Бит размерности ( Default size ) определяет длину адресов и операндов, используемых в команде по умолчанию:

D=\begin</p>
\mbox\\ \mbox \end

Конечно, этот бит предназначен не для обычного пользователя, а для системного программиста, применяющего его, например, для отметки сегментов для сбора"мусора" или сегментов, базовые адреса которых нельзя модифицировать. Этот бит доступен только программам, работающим на высшем уровне привилегий. Микропроцессор в своей работе его не меняет и не использует.

Байт доступа определяет основные правила обращения с сегментом.

Бит присутствия P (Present) показывает возможность доступа к сегменту. Операционная система (ОС) отмечает сегмент, передаваемый из оперативной во внешнюю память , как временно отсутствующий, устанавливая в его дескрипторе P = 0 . При P = 1 сегмент находится в физической памяти. Когда выбирается дескриптор с P = 0 (сегмент отсутствует в ОЗУ ), поля базового адреса и предела игнорируются. Это естественно: например, как может идти речь о базовом адресе сегмента, если самого сегмента вообще нет в оперативной памяти? В этой ситуации процессор отвергает все последующие попытки использовать дескриптор в командах, и определяемое дескриптором адресное пространство как бы"пропадает".

Возникает особый случай неприсутствия сегмента. При этом операционная система копирует запрошенный сегмент с диска в память (при этом, возможно, удаляя другой сегмент), загружает в дескриптор базовый адрес сегмента, устанавливает P = 1 и осуществляет рестарт той команды, которая обратилась к отсутствовавшему в ОЗУ сегменту.

Двухразрядное поле DPL ( Descriptor Privilege Level ) указывает один из четырех возможных (от 0 до 3) уровней привилегий дескриптора, определяющий возможность доступа к сегменту со стороны тех или иных программ (уровень 0 соответствует самому высокому уровню привилегий).

Бит обращения A (Accessed) устанавливается в"1" при любом обращении к сегменту. Используется операционной системой для того, чтобы отслеживать сегменты , к которым дольше всего не было обращений.

Пусть, например, 1 раз в секунду операционная система в дескрипторах всех сегментов сбрасывает бит А. Если по прошествии некоторого времени необходимо загрузить в оперативную память новый сегмент, места для которого недостаточно, операционная система определяет"кандидатов" на то, чтобы очистить часть оперативной памяти, среди тех сегментов, в дескрипторах которых бит А до этого момента не был установлен в"1", то есть к которым не было обращения за последнее время.

Поле типа в байте доступа определяет назначение и особенности использования сегмента. Если бит S ( System - бит 4 байта доступа) равен 1, то данный дескриптор описывает реальный сегмент памяти. Если S = 0 , то этот дескриптор описывает специальный системный объект , который может и не быть сегментом памяти, например, шлюз вызова, используемый при переключении задач, или дескриптор локальной таблицы дескрипторов LDT . Назначение битов <3. 0> байта доступа определяется типом сегмента (рис. 3.4).

Формат поля типа байта доступа

В сегменте кода: бит подчинения, или согласования, C ( Conforming ) определяет дополнительные правила обращения, которые обеспечивают защиту сегментов программ. При C = 1 данный сегмент является подчиненным сегментом кода. В этом случае он намеренно лишается защиты по привилегиям. Такое средство удобно для организации, например, подпрограмм, которые должны быть доступны всем выполняющимся в системе задачам. При C = 0 - это обычный сегмент кода; бит считывания R ( Readable ) устанавливает, можно ли обращаться к сегменту только на исполнение или на исполнение и считывание, например, констант как данных с помощью префикса замены сегмента. При R = 0 допускается только выборка из сегмента команд для их выполнения. При R = 1 разрешено также чтение данных из сегмента.

Запись в сегмент кода запрещена. При любой попытке записи возникает программное прерывание .

В сегменте данных:

  • ED ( Expand Down) - бит направления расширения. При ED = 1 этот сегмент является сегментом стека и смещение в сегменте должно быть больше размера сегмента. При ED = 0 - это сегмент собственно данных (смещение должно быть меньше или равно размеру сегмента);
  • бит разрешения записи W(Writeable) . При W = 1 разрешено изменение сегмента. При W = 0 запись в сегмент запрещена, при попытке записи в сегмент возникает программное прерывание .

В случае обращения за операндом смещение в сегменте формируется микропроцессором по режиму адресации операнда, заданному в команде. Смещение в сегменте кода извлекается из регистра - указателя команд EIP .

Сумма извлеченного из дескриптора начального адреса сегмента и сформированного смещения в сегменте дает линейный адрес (ЛА).

Если в микропроцессоре используется только сегментное представление адресного пространства, то полученный линейный адрес является также и физическим.

Если помимо сегментного используется и страничный механизм организации памяти , то линейный адрес представляется в виде двух полей: старшие разряды содержат номер виртуальной страницы , а младшие смещение в странице. Преобразование номера виртуальной страницы в номер физической проводится с помощью специальных системных таблиц: каталога таблиц страниц (КТС) и таблиц страниц (ТС). Положение каталога таблиц страниц в памяти определяется системным регистром CR3. Физический адрес вычисляется как сумма полученного из таблицы страниц адреса физической страницы и смещения в странице, полученного из линейного адреса.

Рассмотрим теперь все этапы преобразования логического адреса в физический более подробно.

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

И так, чтобы понять организацию памяти от вас потребуется знания некоторых базовых понятий, таких как регистры, стек и тд. Я по ходу попробую объяснить и это на пальцах, но очень кратко потому что это не тема для этой статьи. Итак начнем.

Как известно программист, когда пишет программы работает не с физическим адресом, а только с логическим. И то если он программирует на ассемблере. В том же Си ячейки памяти от программиста уже скрыты указателями, для его же удобства, но если грубо говорить указатель это другое представление логического адреса памяти, а в Java и указателей нет, совсем плохой язык. Однако грамотному программисту не помешают знания о том как организована память хотя бы на общем уровне. Меня вообще очень огорчают программисты, которые не знают как работает машина, обычно это программисты Java и прочие php-парни, с квалификацией ниже плинтуса.

Так ладно, хватит о печальном, переходим к делу.
Рассмотрим адресное пространство программного режима 32 битного процессора (для 64 бит все по аналогии)
Адресное пространство этого режима будет состоять из 2^32 ячеек памяти пронумерованных от 0 и до 2^32-1.
Программист работает с этой памятью, если ему нужно определить переменную, он просто говорит ячейка памяти с адресом таким-то будет содержать такой-то тип данных, при этом сам програмист может и не знать какой номер у этой ячейки он просто напишет что-то вроде:
int data = 10;
компьютер поймет это так: нужно взять какую-то ячейку с номером стопицот и поместить в нее цело число 10. При том про адрес ячейки 18894 вы и не узнаете, он от вас будет скрыт.

Все бы хорошо, но возникает вопрос, а как компьютер ищет эту ячейку памяти, ведь память у нас может быть разная:
3 уровень кэша
2 уровень кэша
1 уровень кэша
основная память
жесткий диск

Это все разные памяти, но компьютер легко находит в какой из них лежит наша переменная int data.
Этот вопрос решается операционной системой совместно с процессором.
Вся дальнейшая статья будет посвящена разбору этого метода.

Архитектура х86 поддерживает стек.

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

push operand
помещает операнд в стек

pop operand
изымает из вершины стека значение и помещает его в свой операнд

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

Теперь кратко рассмотрим что такое регистры.
Это ячейки памяти в самом процессоре. Это самый быстрый и самый дорогой тип памяти, когда процессор совершает какие-то операции со значением или с памятью, он берет эти значения непосредственно из регистров.
В процессоре есть несколько наборов логик, каждая из которых имеет свои машинные коды и свои наборы регистров.
Basic program registers (Основные программные регистры) Эти регистры используются всеми программами с их помощью выполняется обработка целочисленных данных.
Floating Point Unit registers (FPU) Эти регистры работают с данными представленными в формате с плавающей точкой.
Еще есть MMX и XMM registers эти регистры используются тогда, когда вам надо выполнить одну инструкцию над большим количеством операндов.

Рассмотрим подробнее основные программные регистры. К ним относятся восемь 32 битных регистров общего назначения: EAX, EBX, ECX, EDX, EBP, ESI, EDI, ESP
Для того чтобы поместить в регистр данные, или для того чтобы изъять из регистра в ячейку памяти данные используется команда mov:

mov eax, 10
загружает число 10 в регистр eax.

mov data, ebx
копирует число, содержащееся в регистре ebx в ячейку памяти data.

Регистр ESP содержит адрес вершины стека.
Кроме регистров общего назначения, к основным программным регистрам относят шесть 16битных сегментных регистров: CS, DS, SS, ES, FS, GS, EFLAGS, EIP
EFLAGS показывает биты, так называемые флаги, которые отражают состояние процессора или характеризуют ход выполнения предыдущих команд.
В регистре EIP содержится адрес следующей команды, которая будет выполнятся процессором.
Я не буду расписывать регистры FPU, так как они нам не понадобятся. Итак наше небольшое отступление про регистры и стек закончилось переходим обратно к организации памяти.

Как вы помните целью статьи является рассказ про преобразование логической памяти в физическую, на самом деле есть еще промежуточный этап и полная цепочка выглядит так:

Логический адрес --> Линейный (виртуальный)--> Физический

image


Все линейное адресное пространство разбито на сегменты. Адресное пространство каждого процесса имеет по крайней мере три сегмента:
Сегмент кода. (содержит команды из нашей программы, которые будут исполнятся.)
Сегмент данных. (Содержит данные, то бишь переменные)
Сегмент стека, про который я писал выше.

Линейный адрес вычисляется по формуле:
линейный адрес=Базовый адрес сегмента(на картинке это начало сегмента) + смещение
Сегмент кода

Базовый адрес сегмента кода берется из регистра CS. Значение смещения для сегмента кода берется из регистра EIP, в котором хранится адрес инструкции, после исполнения которой, значение EIP увеличивается на размер этой команды. Если команда занимает 4 байта, то значение EIP увеличивается на 4 байта и будет указывать уже на следующую инструкцию. Все это делается автоматически без участия программиста.
Сегментов кода может быть несколько в нашей памяти. В нашем случае он один.
Сегмент данных

Данные загружаются в регистры DS, ES, FS, GS
Это значит что сегментов данных может быть до 4х. На нашей картинке он один.
Смещение внутри сегмента данных задается как операнд команды. По дефолту используется сегмент на который указывает регистр DS. Для того чтобы войти в другой сегмент надо это непосредственно указать в команде префикса замены сегмента.
Сегмент стека

Используемый сегмент стека задается значением регистра SS.
Смещение внутри этого сегмента представлено регистром ESP, который указывает на вершину стека, как вы помните.
Сегменты в памяти могут друг друга перекрывать, мало того базовый адрес всех сегментов может совпадать например в нуле. Такой вырожденный случай называется линейным представлением памяти. В современных системах, память как правило так организована.

Теперь рассмотрим определение базовых адресов сегмента, я писал что они содержаться в регистрах SS, DS, CS, но это не совсем так, в них содержится некий 16 битный селектор, который указывает на некий дескриптор сегментов, в котором уже хранится необходимый адрес.

Так выглядит селектор, в тринадцати его битах содержится индекс дескриптора в таблице дескрипторов. Не хитро посчитать будет что 2^13 = 8192 это максимальное количество дескрипторов в таблице.
Вообще дескрипторных таблиц бывает два вида GDT и LDT Первая называется глобальная таблица дескрипторов, она в системе всегда только одна, ее начальный адрес, точнее адрес ее нулевого дескриптора хранится в 48 битном системном регистре GDTR. И с момента старта системы не меняется и в свопе не принимает участия.
А вот значения дескрипторов могут меняться. Если в селекторе бит TI равен нулю, тогда процессор просто идет в GDT ищет по индексу нужный дескриптор с помощью которого осуществляет доступ к этому сегменту.
Пока все просто было, но если TI равен 1 тогда это означает что использоваться будет LDT. Таблиц этих много, но использоваться в данный момент будет та селектор которой загружен в системный регистр LDTR, который в отличии от GDTR может меняться.
Индекс селектора указывает на дескриптор, который указывает уже не на базовый адрес сегмента, а на память в котором хранится локальная таблица дескрипторов, точнее ее нулевой элемент. Ну а дальше все так же как и с GDT. Таким образом во время работы локальные таблицы могут создаваться и уничтожаться по мере необходимости. LDT не могут содержать дескрипторы на другие LDT.
Итак мы знаем как процессор добирается до дескриптора, а что содержится в этом дескрипторе посмотрим на картинке:
Дескрипторы состоит из 8 байт.
Биты с 15-39 и 56-63 содержат линейный базовый адрес описываемым данным дескриптором сегмента. Напомню нашу формулу для нахождения линейного адреса:

линейный адрес = базовый адрес + смещение
[база; база+предел)

В зависимости от 55 G-бита(гранулярити), предел может измеряться в байтах при нулевом значении бита и тогда максимальный предел составит 1 мб, или в значении 1, предел измеряется страницами, каждая из которых равна 4кб. и максимальный размер такого сегмента будет 4Гб.
Для сегмента стека предел будет в интервале:
(база+предел; вершина]

Кстати интересно почему база и предел так рвано располагаются в дескрипторе. Дело в том что процессоры х86 развивались эволюционно и во времена 286х дескрипторы были по 8 бит всего, при этом старшие 2 байта были зарезервированы, ну а в последующих моделях процессоров с увеличением разрядности дескрипторы тоже выросли, но для сохранения обратной совместимости пришлось оставить структуру как есть.
Значение адреса «вершина» зависит от 54го D бита, если он равен 0, тогда вершина равна 0xFFF(64кб-1), если D бит равен 1, тогда вершина равна 0xFFFFFFFF (4Гб-1)
С 41-43 бит кодируется тип сегмента.
000 — сегмент данных, только считывание
001 — сегмент данных, считывание и запись
010 — сегмент стека, только считывание
011 — сегмент стека, считывание и запись
100 — сегмент кода, только выполнение
101- сегмент кода, считывание и выполнение
110 — подчиненный сегмент кода, только выполнение
111 — подчиненный сегмент кода, только выполнение и считывание

44 S бит если равен 1 тогда дескриптор описывает реальный сегмент оперативной памяти, иначе значение S бита равно 0.

Самым важным битом является 47-й P бит присутствия. Если бит равен 1 значит, что сегмент или локальная таблица дескрипторов загружена в оперативку, если этот бит равен 0, тогда это означает что данного сегмента в оперативке нет, он находится на жестком диске, случается прерывание, особый случай работы процессора запускается обработчик особого случая, который загружает нужный сегмент с жесткого диска в память, если P бит равен 0, тогда все поля дескриптора теряют смысл, и становятся свободными для сохранения в них служебной информации. После завершения работы обработчика, P бит устанавливается в значение 1, и производится повторное обращение к дескриптору, сегмент которого находится уже в памяти.

На этом заканчивается преобразование логического адреса в линейный, и я думаю на этом стоит прерваться. В следующий раз я расскажу вторую часть преобразования из линейного в физический.
А так же думаю стоит немного поговорить о передачи аргументов функции, и о размещении переменных в памяти, чтобы была какая-то связь с реальностью, потому размещение переменных в памяти это уже непосредственно, то с чем вам приходится сталкиваться в работе, а не просто какие-то теоретические измышления для системного программиста. Но без понимания, как устроена память невозможно понять как эти самые переменные хранятся в памяти.
В общем надеюсь было интересно и до новых встреч.

Единственный атрибут сегмента, смысл которого не изменился по сравнению с ре- альным режимом, — адрес сегмента. Однако этот адрес теперь не заносится в сег- ментный регистр процессора, а помещается в дескриптор сегмента, находящийся в оперативной памяти. Адрес сегмента занимает участки At и A2 дескриптора.

Их суммарная длина равна 32 разрядам, что соответствует адресному простран- ству объемом 4 Гбайт.

В отличие от реального режима, сегменты защищенного режима могут иметь лю- бую длину от одного байта до 4 Гбайт.

Длина сегмента, так же как и его адрес, записывается в дескриптор. Включение информации о фактическом размере сегмента в дескриптор обеспечивает возможность аппаратно контролировать ра- боту программы с памятью, предотвращать ее обращение к несуществующим ад- ресам либо по адресам, находящимся вне сегмента. Значение длины сегмента за- нимает участки Lt и L2 дескриптора, суммарная длина которых равна 20 бит. Длина сегмента может измеряться в байтах или страницах, длина которых всегда равна 4 Кбайт. Выбор единицы измерения длины сегмента зависит от значения бита гранулярности G (от лат. granulum — зернышко). При значении G = 0 дли- на сегмента измеряется в байтах, при этом 20-битовое поле длиной L (Lj + L2) обеспечивает возможность задания длины сегмента в пределах от 1 байта до 2 20 байт = 1 Мбайт.

В связи с необходимостью поддерживать совместимость с предшествующими 16-битовыми моделями в дескрипторе сегмента предусмотрен бит разрядности D (от Dimension — измерение), значение которого определяет используемую в сег- менте разрядность операндов и адресов. Если значение D = 0, то используется 16-битовая, а при D = 1 — 32-битовая разрядность адресов и операндов.

В отличие от реального режима, в защищенном режиме возможны только два принципиально разных типа сегментов: сегмент кода и сегмент данных. Тип сег- мента определяется значением бита назначения сегмента I (от Intending — пред- назначение). Если значение I = 0, то дескриптор описывает сегмент данных; если же I = 1, то это сегмент кода.

Сегмент стека считается самостоятельной разновидностью сегмента данных с осо- бым способом изменения его длины. Стек растет в направлении уменьшения адресов оперативной памяти, в то время как у обычной разновидности сегмен- та данных рост происходит в направлении увеличения адресов памяти. В связи с этим разновидность сегмента данных уточняется с помощью бита направления расширения ЕD (от Expand Down — расширение вниз). Если значение ЕD = 0, то сегмент расширяется вниз, то есть в направлении увеличения адресов памяти (обычный сегмент данных). В противном случае, при ЕD = 1, сегмент растет в на- правлении убывания адресов памяти (стек). В дальнейшем изложении вместо оборота «сегмент данных с расширением в направлении убывания адресов» ис- пользуется устоявшееся простое название «сегмент стека».

Любая выполняемая задача может создать и запустить на выполнение некото- рую подчиненную задачу. В связи с этим различают обычные и подчиненные сегменты кода. Обычный сегмент кода содержит программу порождающей зада- чи, а подчиненный сегмент кода — программу порожденной, подчиненной зада- чи.

Разновидность сегмента кода определяется значением подчиненного бита С (от Conforming — подчиненный). Если значение С = 0, то сегмент кода считается подчиненным, а при С = 1 сегмент считается обычным. На самом деле биты ЕD и С — это не два разных бита в дескрипторе, а один и тот же бит, который при- нято обозначать С/ЕD и трактовать его смысл и значение в зависимости от зна- чения бита I.

Программа, занимающая сегмент кода, всегда может быть выполнена. Запись в сегмент кода запрещена, а возможность чтения программного кода из этого сегмента, например с целью его копирования, регулируется битом чтения/записи R/W (от RеаdаЫе/Writеаblе — читаемый/записываемый). Значение R/W = О запрещает чтение, а R/W = 1 — разрешает.

Чтение из сегмента данных разрешено всегда. Возможность записи в этот сег- мент регулируется этим же битом R/W. Значение R/W = 0 запрещает запись, а значение R/W = 1 разрешает ее.

Код типа сегмента I, С/ЕD, R/W является составной частью байта прав доступа АR (от Access Right), входящего в дескриптор сегмента. В этот байт также входят (рис. 6.1) бит присутствия Р (от Present), код уровня привилегий дескриптора DРL (от Descriptor Privilege Level), бит система/сегмент S (от Sуstеm/Sеgmепt) и бит доступа А (от Accessed).

Бит присутствия Р аппаратно получает значение 0, если в текущий момент време- ни сегмент отсутствует в оперативной памяти, и значение 1, если сегмент нахо- дится в ней.

Бит доступа А также аппаратно получает значение 1 при обращении к сегменту (для чтения или записи), в противном случае его значение устанавли- вается в 0. Эти два бита используются механизмами виртуальной памяти.

Управляющая работой компьютера операционная система использует для вы- полнения своих функций различные объекты и структуры, которые так же, как и сегменты оперативной памяти, должны быть описаны. Удобно так организовы- вать работу системы, чтобы все эти описания имели единообразную структуру. В связи с этим разработчиками процессоров Intel было принято решение ис- пользовать для описания различных системных объектов те же самые дескрип- торы, что и для описания сегментов памяти. Разумеется, внутренняя структура дескриптора в этом случае отличается от структуры дескриптора сегмента памя- ти. Для определения роли дескриптора служит бит система/сегмент S, который принимает значение 0, если дескриптор служит для описания системного объек- та, и значение 1, если дескриптор используется для описания сегмента памяти.

В защищенном режиме любая программа не может обращаться к любому сегмен- ту памяти, она должна иметь соответствующие права. Чтобы разграничить воз- можности программ по доступу к сегментам, введено четыре уровня привилегий. Закрепленный за сегментом уровень привилегий отображается в дескрипторе с помощью двухбитового кода уровня привилегий DРL. Наивысший приоритет имеют сегменты нулевого уровня, у которых код DРL равен 002, а минимальный уровень имеют сегменты с кодом DРL, равным 112.

Выполняющаяся программа имеет приоритет, совпадающий с приоритетом сег- мента памяти, в котором она находится. Когда программа обращается к какому- либо сегменту для чтения или записи, то ее приоритет сравнивается с уровнем приоритета сегмента. Если приоритет программы больше приоритета сегмента или равен ему, то обращение разрешается, в противном случае обращение бло- кируется. Например, программа, имеющая DРL = 012, может обращаться за данными к сегментам памяти с DРL = 012, 102 и 112.

Программа с приоритетом DРL = 002 может обращаться к любым сегментам, а программа с DРL = 112 — только к сегментам с таким же приоритетом.

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

Кроме рассмотренных полей и битов в дескрипторе имеется не используемый бит О и используемый программистом по своему усмотрению бит пользовате- ля U (от User). Подробная побитовая структура дескриптора сегмента памяти приведена на рис. 6.1. Инициализация (то есть заполнение разрядов поля значе- ниями) дескриптора сегмента памяти выполняется операционной системой или программой или совместно операционной системой и программой.

Рассмотрим пример, когда программа использует одно адресное пространство.

программа использует одно адресное пространство

Недостатки такой системы:

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

Эти проблемы можно решить, если дать каждому участку независимое адресное пространство, называемое сегментом.

Рассмотрим то же пример с использованием сегментов:

Каждый сегмент может расти или уменьшаться независимо от других.

Сегмент - это логический объект.

В этом случае адрес имеет две части:

адрес в сегменте

Сегменты не мешают друг другу.

Начальный адрес процедуры всегда начинается с (n,0). Что упрощает программирование.

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

Раздельная защита каждого сегмента (чтение, запись).

8.2 Реализация сегментации

Если страницы имеют фиксированный размер, то сегменты нет.

У сегментов так же, как и у страниц, существует проблема фрагментации.

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

8.2.1 Сегментация с использованием страниц: MULTICS

В одной из первых, где была применена страничная сегментация, была система MULTICS .

Каждая программа обеспечивалась до 2^18 сегментов (более 250 000), каждый из которых мог быть до 65 536 (36-разрядных) слов длиной.

Таблица сегментов - хранит дескриптор для каждого сегмента. У каждой программы своя таблица.

Т.к. записей в таблице более 250 000, она сама разбита на страницы.

Сама таблица является отдельным сегментом.

Сегмент с таблицей дескрипторов указывающих на таблицы страниц для каждого сегмента

Нормальный размер страницы равен 1024 словам. Если сегмент меньше 1024, то он либо не разбит на страницы, либо разбит на страницы по 64 слова.

Когда происходит обращение к памяти, выполняется следующий алгоритм:

По номеру сегмента находится дескриптор сегмента.

Проверяется, находиться ли таблица страницы в памяти. Если в памяти, определяется ее расположение. Если нет, вызывается сегментное прерывание.

Проверяется, находиться ли страница в памяти. Если в памяти, определяется ее расположение в памяти. Если нет в памяти, вызывается страничное прерывание.

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

Происходит запись или чтение.

Преобразование адреса в системе MULTICS

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

8.2.2 Сегментация с использованием страниц: Intel Pentium

Каждая программа обеспечивается до 16К сегментов, каждый из которых может быть до 1 млдр 36-разрядных слов длиной.

Основа виртуальной памяти системы Pentium состоит из двух таблиц:

Локальная таблица дескрипторов LDT (Local Descriptor Table) - есть у каждой программы, и описывает сегменты программы.

Глобальная таблица дескрипторов GDT (Global Descriptor Table) - одна для всех программ, и описывает системные сегменты (включая саму ОС).

Каждый селектор (указывает на дескриптор) представляет собой 16-разрядный номер.

Селектор в системе Pentium

13 битов определяют номер записи в таблице дескрипторов, поэтому эти таблицы ограничены, каждая содержит 8К (2^13) сегментных дескрипторов.

1 бит указывает тип используемой таблицы дескрипторов LDT или GDT.

Уровни привилегированности в системе Pentium

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

С учетом максимального размера сегмента - 4 Гбайта - каждая задача, при чисто сегментной организации виртуальной памяти, работает в виртуальном адресном пространстве в 64 Тбайта (4 Гбайта * 16К, где 16К=8К*2 т.к. LDT и GDT).

Дескриптор программного (не данных) сегмента в системе Pentium (всего 8 байт (64 бита)).

База (Base) - базовый адрес сегмента (32-бита), разделен на три части из-за совместимости с i286, в котором это поле имеет только 24 бита.

Размер (Limit) - размер сегмента (20 бит), разнесен на две части.

Если размер сегмента указан в страницах, он может достигать 2^32 байтов (2^20 * 4Кбайт (2^12) (размер страницы в Pentium)).

Алгоритм получение физического адреса:

Селектор загружается в регистр (для сегмента команд в CS, для сегмента данных в DS).

Определяется глобальный или локальный сегмент (LDT или GDT).

Дескриптор извлекается из LDT или GDT, и сохраняется в микропрограммных регистрах.

Если дескриптор в памяти и смещение не выходит за пределы сегмента, программа может продолжить работу, если нет, происходит прерывание.

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

В случае, если используется страничная организация памяти, линейный адрес переводится в физический с помощью таблицы страниц.

Преобразование пары (селектора, смещение) в физический адрес

При 32-разрядном (2^32=4Гбайт) адресе и 4Кбатной странице, сегмент может содержать 1 млн страниц (4Гбайт/4Кбайта). Поэтому используется двухуровневое отображение (создана таблица (страничный каталог) содержащая список из 1024 таблиц страниц), благодаря чему можно снизить количество записей в таблице страниц до 1024.

В этом случае сегмент в 4 Мбайта (1024 записи по 4 Кбайта страницы), будет иметь страничный каталог только с одной записью (и 1024 в таблице страниц), вместо 1 млн в одной таблице.

Отображение линейного адреса на физический адрес

8.3 Особенности реализации в UNIX

В LUNIX системе на 32-разрядной машине каждый процесс получает 3Гбайта виртуального пространства для себя, и 1Гбайт для страничных таблиц и других данных ядра.

На компьютерах Pentium, используется двухуровневые таблицы страниц, и размер страниц фиксирован 4Кбайта

На компьютерах Alpha, используется трехуровневые таблицы страниц, и размер страниц фиксирован 8Кбайт

Сегментные дескрипторы - дескрипторы, описывающие сегменты. Существует три основных типа сегмента:

  • Сегмент кода.
  • Сегмент данных.
  • Сегмент задачи.

Все дескрипторы имеют размер 8 байт и представляются следующим рисунком:

Base G D/B L AVL Limit P DPL S Type Base
Base Limit

В дескрипторе определены следующие поля:

  • Base - база сегмента. 32-битный линейный адрес в памяти, с которого начинается сегмент.
  • Limit - 20-битный лимит сегмента. Максимально допустимое смещение при адресации с использованием этого сегмента.
  • G - бит гранулярности (Granularity) - указывает, в чём измеряется лимит (0 - в байтах, 1 - в страницах по 4 килобайта).
  • D/B - флаг, указывающий разрядность сегмента: 0 – сегмент 16-разрядный, 1 – сегмент 32 разрядный (этот флаг ещё называют BIG).
  • L - флаг, который ранее был зарезервирован, теперь служит признаком 64-разрядности сегмента. Если он установлен, флаг D/B должен быть сброшен.
  • AVL - неиспользуемый бит. Может использоваться по усмотрению ОС.
  • P - признак присутствия сегмента в памяти (сегмент может быть выгружен менеджером виртуальной памяти).
  • DPL - привилегии дескриптора (0 = самые высокие, 3 = самые низкие).
  • S - указывает, является ли дескриптор системным (1 = дескриптор не системный).
  • Segment type - тип сегмента. При S = 0 указывает тип системного сегмента, при S = 1 назначение этого поля поясняется ниже.

Дескриптор сегмента кода

При описании сегмента кода бит S = 1, а поле Segment type имеет вид

  • Бит C (conforming) определяет, является ли сегмент конформным (1 для конформных, 0 для неконформных);
  • Бит R (readable) определяет, доступен ли сегмент для чтения. При R = 1 сегмент доступен для чтения и исполнения, при R = 0 - только для исполнения. Запись в сегмент кода запрещена всегда;
  • Бит A (accessed) устанавливается в единицу при загрузке процессором соответствующего селектора в сегментный регистр. Остается установленным до тех пор, пока не будет явно сброшен операционной системой.

Дескриптор сегмента данных

При описании сегмента данных или стэка бит S = 1, а поле Segment type имеет вид

  • Бит E (expansion direction) определяет направление роста сегмента - 0 для растущих вверх и 1 для растущих вниз (как стэк). Если E = 1, меняется смысл поля Limit, - разрешены обращения к сегменту по смещениям от Limit+1 до 0xFF..F (количество разрядов определяется битами D/B и L);
  • Бит W (writable) разрешает запись в данный сегмент. Если W = 0, разрешено только чтение, если W = 1, разрешены и чтение, и запись. Исполнение данных запрещено всегда. Сегмент стэка всегда должен быть доступен для записи;
  • Бит A (accessed) имеет то же назначение, что и для дескрипторов сегмента кода, - показывает, было ли обращение к дескриптору с момента последнего сброса этого флага.

Дескриптор сегмента задачи

Внимание! Это незаконченная статья. Вы можете помочь доработать статью и наполнить ее материалом.

Читайте также: