Наверх Системное программирование
Предыдущий раздел Оглавление Следующий раздел

4.6. СЕГМЕНТАЦИЯ

До сих пор рассматриваемая виртуальная память была одномерной, поскольку в ней адреса следовали друг за другом от 0 до некоторого максимального значения. Но для решения многих проблем наличие двух и более отдельных виртуальных адресных пространств может быть более рациональным вариантом, чем наличие только одного адресного пространства. Например, у компилятора имеется множество таблиц, выстраиваемых в процессе компиляции, к которым могут относиться:

• исходный текст, сохраненный для печати листинга (в пакетных системах);

• таблица имен, содержащая имена и атрибуты переменных;

• таблица, содержащая все используемые константы, как целочисленные, так и с плавающей точкой;

• дерево разбора, в котором содержится синтаксический анализ программы;

• стек, используемый для вызовов процедур внутри компилятора.

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

Рис

Рис. 4.9. В одномерном адресном
пространстве с разрастающимися
таблицами одна таблица может упереться в другую

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

Простым и универсальным решением является предоставление машины с большим количеством совершенно независимых адресных пространств, называемых сегментами. Каждый сегмент состоит из линейной последовательности адресов от 0 до некоторого максимума. Длина каждого сегмента может иметь любое значение от 0 до максимально разрешенного. Различные сегменты могут быть разной длины, как это обычно и случается. Кроме того, длина сегмента может изменяться в процессе выполнения программы. Длина сегмента стека может увеличиваться при поступлении в него данных и уменьшаться при их извлечении из него.

Поскольку каждый сегмент содержит отдельное адресное пространство, различные сегменты могут разрастаться или сужаться независимо, не влияя друг на друга. Если стек в соответствующем сегменте для того чтобы вырасти, нуждается в дополнительном адресном пространстве, он может его получить, поскольку в его адресном пространстве нет ничего, во что бы он мог упереться. Разумеется, сегмент может заполниться до отказа, но обычно сегменты имеют очень большие размеры, поэтому такое случается крайне редко. Для указания адреса в такой сегментированной, или двумерной, памяти, программа должна предоставить адрес, состоящий из двух частей: номера сегмента и адреса внутри этого сегмента. На рис. 4.10 показана сегментированная память, используемая для рассмотренных ранее таблиц компилятора. На нем показаны пять независимых сегментов.

Рис

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

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

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

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

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

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

Предыдущий раздел Оглавление Следующий раздел