В системах с сегментной и страничной адресацией виртуальный адрес имеет сложную структуру. Он разбит на два битовых поля: селектор страницы (сегмента) и смещение в нем. Соответственно, адресное пространство оказывается состоящим из дискретных блоков. Если все эти блоки имеют фиксированную длину и образуют вместе непрерывное пространство, они называются страницами (рис. 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. Блок-схема алгоритма диспетчера памяти
Видно, что такая схема адресации довольно сложна. Однако в современных
процессорах все это реализовано аппаратно и, благодаря кэшу дескрипторов
и другим ухищрениям, скорость доступа к памяти получается почти такой
же, как и при прямой адресации. Кроме того, эта схема имеет неоценимые
преимущества при реализации многозадачных ОС.
Во-первых, мы можем связать с каждой задачей свою таблицу трансляции,
а значит и свое виртуальное адресное пространство.
Благодаря этому даже в многозадачных ОС мы можем пользоваться абсолютным
загрузчиком. Кроме того, программы оказываются изолированными друг от
друга, и мы можем обеспечить их безопасность.
Во-вторых, мы можем сбрасывать на диск редко используемые области виртуальной
памяти программ — не всю программу целиком, а только ее часть. В отличие
от оверлейных загрузчиков, программа при этом вообще не обязана знать,
какая ее часть будет сброшена.
Другое дело, что в системах реального времени программе может быть нужно,
чтобы определенные ее части никогда не сбрасывались на диск. Система реального
времени обязана гарантировать время реакции, и это гарантированное время
обычно намного меньше времени доступа к диску. Код, обрабатывающий событие,
и используемые при этом данные должны быть всегда в памяти.
В-третьих, программа не обязана занимать непрерывную область физической
памяти. При этом она вполне может видеть непрерывное виртуальное адресное
пространство. Это резко упрощает борьбу с фрагментацией памяти, а в системах
со страничной адресацией проблема внешней фрагментации физической памяти
вообще снимается.
В большинстве систем со страничным диспетчером свободная память отслеживается
при помощи битовой маски физических страниц. В этой маске вободной странице
соответствует 1, а занятой — 0. Если кому-то нужна граница, система просто
ищет в этой маске установленный бит. В результате виртуальное пространство
программы может оказаться отображено на Физические адреса очень причудливым
образом, но это никого не волнует — скорость доступа ко всем страницам
одинакова (рис. 5.4).
Рис. 5.4. Распределение адресного пространства по физической памяти
В-четвертых, система может обеспечивать не только защиту программ друг от друга, но в определенной мере и защиту программы от самой себя — например, от ошибочной записи данных на место кода или попытки исполнить данные. Адресное пространство х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 и т. д.