Сегментная и страничная виртуальная память
В системах с сегментной и страничной
адресацией виртуальный адрес имеет сложную структуру. Он разбит
на два битовых поля: селектор страницы (сегмента)
и смещение в нем. Соответственно, адресное
пространство оказывается состоящим из дискретных блоков. Если все эти
блоки имеют фиксированную длину и образуют вместе непрерывное пространство,
они называются страницами (рис. 5.1).
Рис. 5.1. Страничная виртуальная память, а неиспользуемым
частям блоковыыы
Если длина каждого блока может задаваться, соответствуют "дыры"
в виртуальном адресном пространстве, такие блоки называются сегментами
(рис. 5.2). Как правило, один сегмент соответствует коду или данным одного
модуля программы. Со страницей или сегментом могут быть ассоциированы
права чтения записи и исполнения.
Рис. 5.2. Сегментная виртуальная память
Такая адресация реализуется аппаратно. Процессор имеет специальное устройство,
называемое диспетчером памяти или, как его
называли в старой русскоязычной литературе, УУП (Устройство
Управления Памятью, ср. MMU — Memory Management Unit). В некоторых
процессорах, например в MC68020 или MC68030 или в некоторых RISC-системах,
это устройство реализовано на отдельном кристалле; в других, таких как
х86 или современные RISC-процессоры, диспетчер памяти интегрирован в процессор.
В PDP-11 сегментов всего восемь, поэтому дескрипторы каждого из них размещаются
в отдельном регистре (на самом деле, регистров не восемь, а шестнадцать
— восемь для пользовательского адресного пространства и восемь для системного).
На 32-разрядных машинах количество сегментов измеряется тысячами, а страниц
— иногда и миллионами, поэтому приходится прибегать к более сложной схеме.
Диспетчер памяти содержит регистр — указатель на таблицу
трансляции. Эта таблица размещается где-то в ОЗУ. Ее элементами
являются дескрипторы каждой страницы/сегмента. Такой дескриптор содержит
права доступа к странице, признак присутствия этой страницы в памяти и
физический адрес страницы/сегмента. Для сегментов в дескрипторе также
хранится его длина.
Большинство реальных программ используют далеко не все адресное пространство
процессора, соответственно таблица трансляции не обязательно держит все
допустимые дескрипторы. Поэтому практически все диспетчеры памяти имеют
еще один регистр — ограничитель длины таблицы трансляиии. Страницы или
сегменты, селектор которых превосходит ограничитель, не входят в виртуальное
адресное пространство процесса. Как правило, диспетчер памяти имеет также
кэш (cache) дескрипторов — быструю память
с ассоциативным доступом. В этой памяти хранятся дескрипторы часто используемых
страниц. Алгоритм доступа к памяти по виртуальному адресу page:
off set состоит из следующих шагов (рис. 5.3).
Рис. 5.3. Блок-схема алгоритма диспетчера памяти
- Проверить, существует ли страница page
вообще. Если страницы не существует, возникает особая ситуация
ошибки сегментации (segmentation violation) (понятие особой ситуации
(исключения) подробнее разбирается в Разд.6).
- Попытаться найти дескриптор страницы в кэше.
- Если его нет в кэше, загрузить дескриптор из таблицы
в памяти.
- Проверить, имеет ли процесс соответствующее право доступа
к страпице Иначе также возникает ошибка сегментации.
- Проверить, находится ли страница в оперативной памяти.
Если ее там пет, возникает особая ситуация отсутствия
страницы или страничные отказ (page fault).
Как правило, реакция на нее состоит в том, что вызывается специальная
программа-обработчик (trap — ловушка), которая загружает требуемую страницу
с диска. В многопоточных системах во время такой загрузки может исполняться
другой процесс.
- Если страница есть в памяти, взять из ее дескриптора
физический адрес phys_addr.
- Если мы имеем дело с сегментной адресацией, сравнить
смещение в сегменте с длиной этого сегмента. Если смещение оказаюсь
больше, также возникает ошибка сегментации.
- Произвести доступ к памяти по адресу phys_addr[offset].
Видно, что такая схема адресации довольно сложна. Однако в современных
процессорах все это реализовано аппаратно и, благодаря кэшу дескрипторов
и другим ухищрениям, скорость доступа к памяти получается почти такой
же, как и при прямой адресации. Кроме того, эта схема имеет неоценимые
преимущества при реализации многозадачных ОС.
Во-первых, мы можем связать с каждой задачей свою таблицу трансляции,
а значит и свое виртуальное адресное пространство.
Благодаря этому даже в многозадачных ОС мы можем пользоваться абсолютным
загрузчиком. Кроме того, программы оказываются изолированными друг от
друга, и мы можем обеспечить их безопасность.
Во-вторых, мы можем сбрасывать на диск редко используемые области виртуальной
памяти программ — не всю программу целиком, а только ее часть. В отличие
от оверлейных загрузчиков, программа при этом вообще не обязана знать,
какая ее часть будет сброшена.
Другое дело, что в системах реального времени программе может быть нужно,
чтобы определенные ее части никогда не сбрасывались на диск. Система реального
времени обязана гарантировать время реакции, и это гарантированное время
обычно намного меньше времени доступа к диску. Код, обрабатывающий событие,
и используемые при этом данные должны быть всегда в памяти.
В-третьих, программа не обязана занимать непрерывную область физической
памяти. При этом она вполне может видеть непрерывное виртуальное адресное
пространство. Это резко упрощает борьбу с фрагментацией памяти, а в системах
со страничной адресацией проблема внешней фрагментации физической памяти
вообще снимается.
В большинстве систем со страничным диспетчером свободная память отслеживается
при помощи битовой маски физических страниц. В этой маске вободной странице
соответствует 1, а занятой — 0. Если кому-то нужна граница, система просто
ищет в этой маске установленный бит. В результате виртуальное пространство
программы может оказаться отображено на Физические адреса очень причудливым
образом, но это никого не волнует — скорость доступа ко всем страницам
одинакова (рис. 5.4).
Рис. 5.4. Распределение адресного пространства по физической
памяти
В-четвертых, система может обеспечивать не только защиту программ друг
от друга, но в определенной мере и защиту программы от самой себя — например,
от ошибочной записи данных на место кода или попытки исполнить данные.
В-пятых, разные задачи могут использовать общие области памяти для взаимодействия
или, скажем, просто для того, чтобы работать с одной копией библиотеки
подпрограмм.
Перечисленные преимущества настолько серьезны, что считается невозможным
реализовать многозадачную систему общего назначения, такую как UNIX или
System/390 на машинах без диспетчера памяти.
Для систем реального времени, впрочем, виртуальная память оказывается
скорее вредна, чем бесполезна: наличие диспетчера памяти увеличивает объем
контекста процесса (это понятие подробнее обсуждается в разд. Вытесняющая
многозадачность), воспользоваться же главным преимуществом — возможностью
страничного обмена — задачи реального времени в полной мере не могут.
Поэтому такие системы, даже работающие на процессорах со встроенным диспетчером
памяти, часто этот диспетчер не используют. Даже если виртуальная память
доступна, система РВ (реального времени) обязана предоставлять средства
блокировки кода и данных (не обязательно всех) пользовательского процес
са в физической памяти.
Отдельной проблемой при разработке системы со страничной или сегментной
адресацией является выбор размера страницы или максимального размера сегмента.
Этот размер определяется шириной соответствующего битового поля адреса
и поэтому должен быть степенью двойки.
С одной стороны, страницы не должны быть слишком большими, так как это
может привести к внутренней фрагментации и перекачке слишком больших объемов
данных при сбросе страниц на диск. С другой стороны, страницы не должны
быть слишком маленькими, так как это приведет к чрезмерному увеличению
таблиц трансляции, требуемого объема кэша дескрипторов и т. д.
В реальных системах размер страницы меняется от 512 байт у машин семейства
VAX до нескольких килобайт. Например, х86 имеет страницу размером 4 Кбайт.
Некоторые диспетчеры памяти, например у МС6801/2/30, имеют переменный
размер страницы — система при запуске программирует диспетчер и устанавливает,
помимо прочего, этот размер, и дальше работает со страницами выбранного
размера. У процессора i860 размер страницы переключается между 4 Кбайтами
и 4 Мбайтами.
С сегментными диспетчерами памяти ситуация сложнее. С одной стороны, хочется,
чтобы один программный модуль помешался в сегмент, поэтому сегменты обычно
делают большими, от 32 Кбайт и более. С другой стороны, хочется, чтобы
в адресном пространстве можно было сделать много сегментов. Кроме того,
может возникнуть проблема: как быть с большими неразделимыми объектами,
например хэш-таблицами компиляторов, под которые часто выделяются сотни
килобайт.
В результате ряд машин предоставляет двухступенчатую виртуальную память
— сегментную адресацию, в которой каждый сегмент, в свою очередь, разбит
на страницы. Это дает ряд мелких преимуществ, например, позволяет давать
права доступа сегментам, а подкачку с диска осуществлять постранично.
Таким образом, организована виртуальная память в IBM System 370 и ряде
других больших компьютеров, а также в х86. Правда, в последнем виртуальная
память используется несколько странным образом.
Адресное пространство х86
х86 может работать с двумя типами адресов:
32-разрядным адресом, в котором 16 бит задают
смещение в сегменте, 14 бит— номер сегмента и 2 бита используются для
разных загадочных целей. При этом размер сегмента не более 64 Кбайт, а
общий объем виртуальной памяти не превышает 1 Гбайта.
48-разрядным адресом, в котором смещение в
сегменте занимает 32 бита. В этом случае размер сегмента может быть до
4 Гбайт, а общий объем виртуальной памяти до 244 байт. В обоих случаях
сегмент может быть разбит на страницы по 4 Кбайт.
При этом сегментная часть адреса и его смещение лежат в разных регистрах,
и с ними можно работать раздельно. В реальном режиме возможность такой
работы порождает весь "зоопарк моделей памяти", с которыми знакомы
те, кто писал на С для MS DOS. В защищенном режиме х86 большинство систем
программирования выделяют программе один сегмент с 32-разрядным смещением,
и программа живет там так, будто это обычная машина с 32-разрядным линейным
адресным пространством. Так поступают все известные авторам реализации
Unix для х86, ряд так называемых расширителей ДОС (DOS extenders), Oberon/386,
Novell Netware, реализации Win32 и т. д.
|