Добавлен: 29.10.2018
Просмотров: 48087
Скачиваний: 190
276
Глава 3. Управление памятью
файловой системы. Именно такой подход используется в Windows. Но для уменьше-
ния объема необходимого дискового пространства здесь может быть использована
оптимизация. Поскольку текст программы каждого процесса берется из какого-нибудь
исполняемого файла, принадлежащего файловой системе, этот исполняемый файл
может быть использован в качестве области подкачки. Еще лучше то, что, поскольку
текст программы обычно имеет статус «Только для чтения», когда в памяти становит-
ся тесно и страницы программы должны быть из нее удалены, они просто считаются
уничтоженными и считываются снова из исполняемого файла по мере надобности.
Таким же образом можно работать и с совместно используемыми библиотеками.
3.6.6. Разделение политики и механизма
Важным инструментом, позволяющим справиться со сложностью любой системы, яв-
ляется отделение политики от механизма. Этот принцип может быть применен к управ-
лению памятью за счет запуска основной части диспетчера памяти как процесса на
уровне пользователя. Подобное разделение впервые было предпринято в системе Mach
(Young et al., 1987), на которой построено дальнейшее рассмотрение этого вопроса.
Простой пример того, как могут быть разделены политика и механизм, показан на
рис. 3.27. Здесь система управления памятью разделена на три части:
низкоуровневую программу управления диспетчером памяти (MMU);
обработчик ошибки отсутствия страницы, являющийся частью ядра;
внешнюю программу страничной организации памяти, запущенную в пользова-
тельском пространстве.
Рис. 3.27. Обработка ошибки отсутствия страницы с использованием внешней системы
страничной организации памяти
Все тонкости работы MMU скрыты в программе управления этим диспетчером памяти,
код которой является машинозависимым и должен переписываться для каждой новой
платформы, на которую переносится операционная система. Обработчик ошибки от-
сутствия страницы является машинонезависимым кодом и содержит основную часть
механизма страничной организации памяти. Политика определена в основном внеш-
3.7. Сегментация
277
ней системой страничной организации памяти, запускаемой в виде пользовательского
процесса.
При запуске процесса уведомляется внешняя программа страничной организации,
чтобы установить отображение страниц процесса и выделить в случае необходимости
резервное хранилище на диске. Во время работы процесс может отображать в своем
адресном пространстве новые объекты, о чем опять же уведомляется внешняя про-
грамма.
Как только процесс начнет работу, он может столкнуться с ошибкой отсутствия страни-
цы. Обработчик ошибки определяет, какая виртуальная страница требуется процессу,
и посылает сообщение внешней программе, сообщая ей о возникшей проблеме. Затем
эта внешняя программа считывает нужную страницу с диска и копирует ее в раздел
собственного адресного пространства. Затем она сообщает обработчику, где находится
страница. После этого обработчик ошибки удаляет отображение страницы из адресного
пространства внешней программы и просит управляющую программу MMU поместить
ее в нужное место адресного пространства пользователя. Затем пользовательский про-
цесс может быть перезапущен.
Эта реализация оставляет открытым вопрос, куда поместить алгоритм замещения
страниц. Казалось бы, ясно, что он должен быть во внешней программе, но реализация
такого подхода сталкивается с рядом проблем. Самой принципиальной из них явля-
ется то, что внешняя программа управления страницами не имеет доступа к битам R
и M всех страниц, а эти биты играют важную роль во многих алгоритмах замещения
страниц. Таким образом, либо необходим какой-нибудь механизм для передачи этой
информации вверх внешней программе управления, либо алгоритм замещения страниц
должен быть помещен в ядро. В последнем варианте обработчик ошибки сообщает
внешней программе управления, какую страницу он выбрал для удаления, и предо-
ставляет данные, либо отображая эту страницу в адресном пространстве внешней
программы управления, либо включая их в сообщение. В любом случае внешняя про-
грамма управления записывает данные на диск.
Основным преимуществом такой реализации является большая модульность кода и бо-
лее высокая степень гибкости. Основной недостаток заключается в дополнительных
издержках на неоднократные пересечения границы пользовательского пространства
и пространства ядра, а также в издержках на пересылку между частями системы раз-
личных сообщений. Сейчас вопрос носит весьма спорный характер, но по мере того
как компьютеры становятся все более быстродействующими, а программное обеспече-
ние — все более сложным, вероятно, большинству разработчиков будет лучше пожерт-
вовать какой-то долей производительности в пользу более надежного программного
обеспечения.
3.7. Сегментация
До сих пор рассматриваемая виртуальная память была одномерной, поскольку в ней
адреса следовали друг за другом от 0 до некоторого максимального значения. Но для
решения многих проблем наличие двух и более отдельных виртуальных адресных
пространств может быть более рациональным вариантом, чем наличие только одного
адресного пространства. Например, у компилятора имеется множество таблиц, вы-
страиваемых в процессе компиляции, к которым могут относиться:
278
Глава 3. Управление памятью
исходный текст, сохраненный для печати листинга (в пакетных системах);
таблица имен, содержащая имена и атрибуты переменных;
таблица, содержащая все используемые константы, как целочисленные, так и с пла-
вающей точкой;
дерево разбора, в котором содержится синтаксический анализ программы;
стек, используемый для вызовов процедур внутри компилятора.
В процессе компиляции каждая из первых четырех таблиц постоянно растет. А послед-
няя увеличивается и уменьшается в размерах совершенно непредсказуемым образом.
В одномерной памяти этим пяти таблицам должны быть выделены последовательные
участки виртуального адресного пространства, показанные на рис. 3.28.
Рис. 3.28. В одномерном адресном пространстве с разрастающимися таблицами
одна таблица может упереться в другую
Рассмотрим, что получится, если программа содержит намного большее, чем обычно,
количество переменных, но вполне обычное количество всех остальных компонентов.
Участок адресного пространства, выделенный под таблицу имен, может заполниться
до отказа, но для других таблиц может остаться большое количество свободного про-
странства.
На самом деле здесь нужен способ избавить программиста от необходимости усмирения
увеличивающихся и уменьшающихся в размерах таблиц аналогично тому, как вирту-
альная память устраняет беспокойство по поводу организации программы в оверлеи.
Простым и универсальным решением является предоставление машины с большим
количеством совершенно независимых адресных пространств, называемых сегментами.
Каждый сегмент состоит из линейной последовательности адресов от 0 до некоторого
максимума. Длина каждого сегмента может иметь любое значение от 0 до максимально
разрешенного. Различные сегменты могут быть разной длины, как это обычно и слу-
3.7. Сегментация
279
чается. Кроме того, длина сегмента может изменяться в процессе выполнения про-
граммы. Длина сегмента стека может увеличиваться при поступлении в него данных
и уменьшаться при их извлечении из него.
Поскольку каждый сегмент содержит отдельное адресное пространство, различные сег-
менты могут разрастаться или сужаться независимо, не влияя друг на друга. Если стек
в соответствующем сегменте, для того чтобы вырасти, нуждается в дополнительном
адресном пространстве, он может его получить, поскольку в его адресном пространстве
нет ничего, во что бы он мог упереться. Разумеется, сегмент может заполниться до
отказа, но обычно сегменты имеют очень большие размеры, поэтому такое случается
крайне редко. Для указания адреса в такой сегментированной, или двумерной, памяти,
программа должна предоставить адрес, состоящий из двух частей: номера сегмента
и адреса внутри этого сегмента. На рис. 3.29 показана сегментированная память, ис-
пользуемая для рассмотренных ранее таблиц компилятора. На нем показаны пять
независимых сегментов.
Рис. 3.29. Сегментированная память дает возможность каждой таблице разрастаться
или сужаться независимо от всех остальных таблиц
Стоит подчеркнуть, что сегмент — это логический объект. Программист знает это
и использует его именно в этом качестве. Сегмент может содержать процедуру, или
массив, или стек, или набор скалярных переменных, но обычно он не содержит смесь
из разнотипных данных.
Сегментированная память помимо упрощения обращения с разрастающимися или
сужающимися структурами данных имеет и другие преимущества. Если каждая про-
цедура занимает отдельный сегмент, имея 0 в качестве начального адреса, то компо-
новка отдельно скомпилированных процедур существенно упрощается. После того как
все составляющие программу процедуры скомпилированы и скомпонованы, в вызове
процедуры, обращенном к процедуре, в сегменте n будет использован адрес, состоящий
из двух частей (n, 0) и адресованный к слову 0 (к точке входа).
280
Глава 3. Управление памятью
Если впоследствии процедура в сегменте n будет изменена и перекомпилирована, то
изменять другие процедуры уже не придется (поскольку начальные адреса не будут
изменены), даже если новая версия будет больше старой. При использовании одно-
мерной памяти процедуры компонуются непосредственно друг за другом, без какого-
либо адресного пространства между ними. Следовательно, изменение размеров одной
процедуры повлияет на начальные адреса других (не связанных с ней) процедур
в сегменте. А это, в свою очередь, потребует изменения всех процедур, из которых вы-
зываются любые перемещенные процедуры, чтобы учесть их новые начальные адреса.
Если программа содержит несколько сотен процедур, этот процесс может стать весьма
затратным.
Сегментация также облегчает совместное использование процедур или данных не-
сколькими процессами. Типичным примером может послужить совместно используе-
мая библиотека. Современные рабочие станции, работающие с передовыми оконными
системами, зачастую используют весьма объемные графические библиотеки, откомпи-
лированные чуть ли не в каждой программе. В сегментированной системе графические
библиотеки могут быть помещены в сегмент и совместно использоваться несколькими
процессами, исключая потребность в своем присутствии в адресном пространстве каж-
дого процесса. Хотя совместно используемые библиотеки можно иметь и в системах,
построенных только на страничной организации памяти, но в них это достигается зна-
чительно сложнее. В сущности, в этих системах подобное использование реализуется
путем моделирования сегментации.
Поскольку каждый сегмент формирует известный программисту логический объект,
например процедуру, или массив, или стек, у разных сегментов могут быть разные виды
защиты. Сегмент процедуры может быть определен только как исполняемый, с запре-
щением попыток что-либо в нем прочитать или сохранить. Массив чисел с плавающей
точкой может быть определен для чтения и записи, но не для выполнения, и попытки
передать ему управление будут отловлены. Подобная защита весьма полезна при вы-
явлении ошибок программирования.
Сравнение страничной организации памяти и сегментации приведено в табл. 3.3.
Таблица 3.3. Сравнение страничной организации памяти и сегментации
Вопрос
Страничная
организация
Сегментация
Нужно ли программисту знать, что ис-
пользуется именно эта технология?
Нет
Да
Сколько имеется линейных адресных
пространств?
1
Много
Может ли все адресное простран-
ство превысить размер физической
памяти?
Да
Да
Могут ли различаться и быть отдельно
защищены процедуры и данные?
Нет
Да
Можно ли без особого труда предоста-
вить пространство таблицам, изменя-
ющим свой размер?
Нет
Да
Облегчается ли для пользователей со-
вместный доступ к процедурам?
Нет
Да