Э. Таненбаум - Архитектура компьютера (1127755), страница 122
Текст из файла (страница 122)
Виртуальная память 487 Каждая из первых четырех структур постоянно растет в процессе компиляции. Размер последней структуры меняется совершенно непредсказуемо. В одномерной памяти эти пять структур пришлось бы разместить в виртуальном адресном пространстве в виде смежных областей, как показано на рис. 6.6. Посмотрим, что произойдет, если программа содержит очень большое число переменных. Область адресного пространства, предназначенная для таблицы символов, может переполниться, даже если в других таблицах полно свободного места. Компилятор, конечно, может сообщить, что он не способен продолжать работу из-за большого количества переменных, но можно без этого и обойтись, поскольку в других таблицах много свободного места. Виртуальное адресное пространство Адресное пространство, предназначенное для стека вызовов Свободно ( Занято Рис.
6.6. Если е одномерном адресном пространстве размер структур постоянно растет, одна из ник может «врезаться» в другую Компилятор может забирать свободное пространство у одних структур и передавать другим, но это будет напоминать управление оверлеями вручную — некоторое неудобство в лучшем случае и долгая нудная работа в худшем. На самом деле нужно просто освободить программиста от необходимости думать о расширении и сокращении структур, подобно тому, как виртуальная память позволяет забыть о разбиении программы на оверлеи. Для этого нужно создать множество абсолютно независимых адресных пространств, которые называются сегментами. Каждый сегмент состоит из линейной последовательности адресов от 0 до какого-либо допустимого максимума.
Разные сегменты могут иметь разную длину г',обычно так и бывает). Более того, длина сегмента может меняться во время выполнения программы. Длина стекового сегмента может увеличиваться всякий раз, когда что-либо помещается в стек, и уменьшаться, когда что-либо выталкивается из стека. Так как каждый сегмент образует отдельное адресное пространство, разные сегменты могут увеличиваться или уменьшаться независимо друг от друга и не влияя друг на друга. Если стеку в определенном сегменте потребуется больше адресного пространства (чтобы стек мог расти), он может получить его, поскольку 488 Глава 6.
Уровень операционной системы его адресному пространству больше не во что «врезаться». Естественно, сегмент может переполниться, но это происходит редко, поскольку сегменты очень большие. Чтобы задать адрес в двухмерной памяти, программа должна указать номер сегмента и адрес внутри сегмента. Сегментированная память изображена на рис. 6.7. 2ОК 16К 12К 6К о Исходный текст Ю~ ~ Дерево ТаблиЦа разбора констант О Сегмент О Сегмент 1 Сегмент 2 Сегмент 3 Сегмент 4 Рис. 6.?.
Сегментированная память позволяет менять размер каждой структуры независимо от других Подчеркнем, что сегмент является логическим элементом, о котором программист знает и который он использует. Сегмент может содержать процедуру, массив, стек или ряд скалярных переменных, но обычно в него входит только один тип данных. Сегментированная память имеет и другие преимущества, помимо упрощения работы со структурами данных, которые постоянно меняются в размерах. Если каждая процедура занимает отдельный сегмент, в котором первый адрес — это адрес О, то сборка процедур, скомпилированных раздельно, значительно упрощается.
Когда все процедуры программы скомпилированы и собраны, при вызове процедуры из сегмента и для обращения к слову 0 будет использоваться адрес (и, 0). Если процедура в сегменте п впоследствии будет изменена и перекомпилирована, то другие процедуры менять не потребуется (поскольку их начальные адреса не изменятся), даже если новая версия окажется больше по размеру, чем предыдущая.
В одномерной памяти процедуры обычно располагаются друг за другом, и между ними нет свободного адресного пространства. Следовательно, изменение размера одной процедуры может повлиять на начальные адреса других процедур. Это, в свою очередь, потребует изменения всех других процедур, которые вызывают любую из указанных. Если в программу включено множество процедур, переделка оказывается весьма трудоемкой. Сегментация облегчает совместное использование процедур или данных несколькими программами.
Если компьютер содержит несколько программ, работающих параллельно (зто может быть реальный или моделируемый параллелизм), и все эти программы вызывают одни и те же процедуры, снабжать каждую программу отдельными копиями процедур расточительно для памяти. Если же сделать каждую процедуру отдельным сегментом, их легко можно будет использовать Виртуальная память 489 совместно, что исключит необходимость создавать физические копии каждой разделяемой процедуры. В результате мы сэкономим память, Поскольку каждый сегмент являет собой логический объект (например, процедуру, массив или стек), о существовании которого знает программист, разные сегменты можно защищать по-разному.
Например, сегменту с процедурой можно назначить атрибут чтолько для выполнения», запретив тем самым считывание из него и запись в него. Для массива с плавающей точкой можно разрешить только чтение и запись, но не выполнение и т. д. Такая защита часто помогает выявлять программные ошибки. Нужно понимать, почему защита имеет смысл только для сегментированной памяти и не имеет никакого смысла для одномерной (линейной) памяти. Обычно в сегменте не могут находиться одновременно процедура и стек (только что-нибудь одно).
Поскольку каждый сегмент содержит объект только одного типа, он может использовать защиту, подходящую для этого типа. Страничная организация памяти и сегментация сравниваются в табл. 6,1. Таблица 6.1. Сравнение страничной организации памяти и сегментации Страничная Сегментация организация памяти Свойство Нет Необходимость учета программистом Да Количество линейных адресных пространств Много Возможность увеличения памяти для виртуального адресного пространств Да Простота управления структурами переменного размера Да Нет Назначение технологии Имитация памяти большого размера Предоставление нескольких адресных пространств Реализация сегментации Сегментацию можно реализовать одним из двух способов: подкачкой сегментов и разбиением памяти на страницы.
При первом подходе в памяти находится некоторый набор сегментов. Если происходит обращение к сегменту, которого нет Содержимое страницы в каком-то смысле случайно. Программист может ничего не знать о страничной организации памяти. В принципе можно поместить несколько битов в каждый элемент таблицы страниц для доступа к нему, но чтобы использовать эту особенность, программисту придется следить за границами страницы в адресном пространстве. Но ведь механизм разбиения памяти на страницы был придуман именно для устранения подобных трудностей. Что же касается сегментации, то поскольку у пользователя создается иллюзия, что все сегменты постоянно находятся в основной памяти, к ним можно обращаться и не следить при этом за оверлеями.
490 Глава 6. Уровень операционной системы в памяти, этот сегмент переносится в память. Если для него нет места в памяти, один или несколько сегментов сначала сбрасываются на диск (если на диске уже есть их копия, сегменты просто удаляются из памяти). В каком-то смысле подкачка сегментов очень похожа на вызов страниц по требованию: сегменты загружаются и удаляются только в случае необходимости. Однако сегментация существенно отличается от разбиения на страницы в следующем; размер страниц фиксирован, а размер сегментов — нет.
На рис. 6.8, а показан пример физической памяти, в которой изначально содержится 5 сегментов. Посмотрим, что происходит, если сегмент 1 удаляется, а сегмент 7, который меньше по размеру, помещается на его место. В результате получается конфигурация, изображенная на рис. 6.8, б. Между сегментом 7 и сегментом 2 оказывается неиспользованная (епустая») область. Затем сегмент 4 заменяется сегментом 5 (рис. 6.8, в), а сегмент 3 — сегментом 6 (рис. 6.8, г). Через некоторое время память разделится на ряд областей, одни из которых будут содержать сегменты, а другие — пустбты.
Это называется внешней фрагментацией (поскольку неиспользованное пространство попадает не в сегменты, а в пустоты между ними, то есть процесс происходит вне сегментов). Иногда внешнюю фрагментацию называют поклеточной разбивкой (сЬес1гегЪоагс(1пй). Рив. 6.8. Динамика внешней фрагментации (в, б, в, г); дефрагментация путем уплотнения (д) Посмотрим, что произойдет, если программа обратится к сегменту 3, когда память фрагментирована (см. рис. 6.8, г). Общее пространство пустот составляет 10 Кбайт, и, хотя это больше, чем нужно для сегмента 3, сегмент 3 туда не помещается, так как пространство разбито на маленькие фрагменты.
Вместо этого приходится сначала удалять другой сегмент. Виртуальная память 491 Чтобы избежать подобной ситуации, нужно каждый раз при появлении пустого пространства перемещать следующие сегменты ближе к адресу О, удаляя таким образом это пустое пространство (точнее, «сдвигая» его к концу памяти). Есть и другой способ. Можно подождать, пока внешняя фрагментация не станет серьезно влиять на процессы (когда на долю пустот придется больше некоторого процента от всего объема памяти), и только после этого выполнить уплотнение. На рис.
6.8, д показано, как память будет выглядеть после уплотнения. Цель уплотнения памяти — собрать все маленькие пустоты в одно большое свободное пространство, в котороепомещается один или несколько сегментов. Недостаток уплотнения состоит в том, что на этот процесс тратится некоторое время. Если на уплотнение памяти требуется слишком много времени, нужен специальный алгоритм для определения, какие именно пустоты лучше использовать для определенного сегмента. Для этого требуется список адресов и размеров всех пустот.
В популярном алгоритме оптимальной подгонки (Ьезг 61) выбирается самая маленькая из пустот, в которую помещается нужный сегмент. Цель этого алгоритма — не допустить использования большого свободного фрагмента, который может понадобиться позже для большого сегмента. В другом популярном алгоритме список пустот просто просматривается по кругу и выбирается первый же свободный фрагмент, по размеру подходящий для данного сегмента. Удивительно, но последний алгоритм с точки зрения общей производительности работает гораздо лучше, чем алгоритм оптимальной подгонки, поскольку последний порохсдает очень много маленьких пустот [1141.
Оба алгоритма сокращают средний размер свободного фрагмента. Всякий раз, когда сегмент помещается в свободный фрагмент, превышающий по размеру этот сегмент, что бывает практически всегда (точные попадания очень редки), свободный фрагмент делится на две части.