Глава 10

Глава 10. Аппаратно-независимый уровень управления виртуальной памятью

Предыдущая глава | Программа курса | Следующая глава

Обычно ОС опирается на некоторое собственное представление организации виртуальной памяти, которое используется в аппаратно-независимой части подсистемы управления виртуальной памятью и связывается с конкретной аппаратной реализацией с помощью аппаратно-зависимой части.

Как же достигается возможность наличия виртуальной памяти с размером, существенно превышающим размер оперативной памяти? В элементе таблицы страниц может быть установлен специальный флаг (означающий отсутствие страницы), наличие которого заставляет аппаратуру вместо нормального отображения виртуального адреса в физический прервать выполнение команды и передать управление соответствующему компоненту операционной системы. Когда программа обращается к виртуальной странице, отсутствующей в основной памяти, т.е. "требует" доступа к данным или программному коду, операционная система удовлетворяет это требование путем выделения страницы основной памяти, перемещения в нее копии страницы, находящейся во внешней памяти, и соответствующей модификации элемента таблицы страниц. Здесь мы имеем дело с частным случаем исключительной ситуации (exception) при работе с памятью, так называемым страничным нарушением (page fault).

10.1 Исключительные ситуации при работе с памятью.

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

Естественно, что операционная система должна быть как-то оповещена о происшедшем. Обычно для этого используется механизм исключительных ситуаций (exceptions). При попытке выполнить операцию такого рода, возникает исключительная ситуация страничное нарушение (page fault), приводящая к вызову специальной программы - обработчика (handler) этой исключительной ситуации, который уже и решает, что же нужно предпринять, чтобы обработать конкретный вид страничного нарушения. Страничное нарушение обычно происходит в самых разных случаях, например: при попытке записи в страницу с атрибутом "только чтение" или при попытке чтения или записи страницы с атрибутом "только выполнение". В любом из этих случаев вызывается обработчик страничного нарушения, обычно являющийся частью операционной системы, которому обычно передается причина возникновения исключительной ситуации и соответствующий виртуальный адрес.

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

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

Первое и третье времена могут быть уменьшены за счет тщательного кодирования нескольких сотен инструкций и составлять десятки микросекунд каждое. Время подкачки страницы с диска будет вероятно близким к нескольким десяткам миллисекунд. Оценки (Silberschatz) показывают, что если удастся снизить вероятность page fault'а до 5*10**-7, то производительность страничной системы будет снижена всего на 10%. Таким образом, снижение частоты page fault'ов является одной из ключевых задач системы управления памятью. Ее решение обычно связано с правильным выбором алгоритма замещения страниц.

10.2 Стратегии управления страничной памятью

Обычно рассматривают три стратегии:

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

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

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

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

10.3 Алгоритмы замещения страниц

Итак, наиболее ответственным действием страничной системы является выделение страницы основной памяти для удовлетворения требования доступа к отсутствующей в основной памяти виртуальной странице. Напомним, что мы рассматриваем ситуацию, когда размер каждой виртуальной памяти может существенно превосходить размер основной памяти. Это означает, что при выделении страницы основной памяти с большой вероятностью не удастся найти свободную (не приписанную к какой-либо виртуальной памяти) страницу. В этом случае операционная система должна в соответствии с заложенными в нее критериями найти некоторую занятую страницу основной памяти, переместить в случае надобности ее содержимое во внешнюю память, должным образом модифицировать соответствующий элемент соответствующей таблицы страниц и после этого продолжить процесс удовлетворения доступа к странице.

Заметим, что при замещении приходится дважды передавать страницу между основной и вторичной памятью. Процесс замещения может быть оптимизирован за счет использования бита модификации (один из атрибутов страницы). Бит модификации устанавливается компьютером, если хотя бы один байт записан на страницу. При выборе кандидата на замещение, проверяется бит модификации. Если бит не установлен, нет необходимости переписывать данную страницу на диск, она уже там. Эта техника также применяется к read-only страницам, они никогда не модифицируются. Эта схема уменьшает время обработки fault'а.

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

Глобальные алгоритмы имеют несколько недостатков. Во-первых, они делают одни процессы чувствительными к поведению других процессов. Например, если один процесс в системе использует большое количество памяти, то все остальные приложения будут в результате ощущать сильное замедление из-за недостатка памяти. Во-вторых, некорректно работающее приложение может подорвать работу всей системы (если конечно в системе не предусмотрено ограничение на размер памяти, выделяемой процессу), пытаясь захватить все больше памяти. Поэтому в многозадачной системе лучше использовать более сложные, но эффективные локальные алгоритмы. Такой подход требует, чтобы система хранила список физических страниц каждого процесса. Этот список страниц иногда называют рабочим множеством процесса. Рабочее множество и реализация алгоритма подкачки, основанного на понятиях локальности и рабочего множества описаны в последующих разделах.

Алгоритм обычно оценивается на конкретной последовательности ссылок к памяти, для которой подсчитывается число fault'ов. Эта последовательность называется reference string. Мы можем генерировать reference string искусственным образом при помощи датчика случайных чисел или трассируя конкретную систему. Последний метод дает слишком много ссылок, для уменьшения числа которых можно сделать две вещи:

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

Как уже говорилось, большинство процессоров имеют простейшие аппаратные средства, позволяющие собирать некоторую статистику обращений к памяти. Эти средства включают два специальных флага на каждый элемент таблицы страниц. Один флаг (флаг обращения, reference бит) автоматически устанавливается, когда происходит любое обращение к этой странице, а второй флаг (флаг изменения, modify бит) устанавливается, если производится запись в эту страницу. Чтобы использовать эти возможности, операционная система должна периодически сбрасывать эти флаги.

10.3.1 FIFO алгоритм. Выталкивание первой пришедшей страницы.

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

Аномалия Belady

Интуитивно ясно, что чем больше страничных кадров имеет память, тем реже будут иметь место page fault'ы. Удивительно, но это не всегда так. Как установил Belady с коллегами, определенные последовательности обращений к страницам приводят в действительности к увеличению числа страничных нарушений при увеличении кадров, выделенных процессу. Это явление носит название аномалии FIFO.

Три кадра (9 faults) оказываются лучше чем 4 кадра (10 faults) для 012301401234 цепочки чередования страниц при выборе стратегии FIFO.

Рис. 10.1 Аномалия Belady. (a) FIFO с тремя страничными кадрами. (b) FIFO с четырьмя страничными кадрами.

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

10.3.2 Оптимальный алгоритм

Одно из последствий открытия аномалии Belady - поиск оптимального алгоритма. Этот алгоритм имеет минимальную частоту fault'ов среди всех алгоритмов. Он прост:

замещай страницу, которая не будет использоваться в течение длительного периода времени.

Каждая страница помечается числом инструкций, которые будут выполнены, прежде чем на эту страницу будет сделана первая ссылка.

Этот алгоритм нереализуем. ОС не знает, к какой странице будет следующее обращение. (Ранее такие проблемы были с планированием процессов - алгоритм SJF). Для второго обращения уже можно делать прогноз на основе информации собранной после первого обращения.

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

10.3.3 Выталкивание дольше всего не использовавшейся страницы. LRU (The Least Recently Used) Algorithm .

Исходим из эвристического правила, что недавнее прошлое - хороший ориентир для прогнозирования ближайшего будущего.

Ключевое отличие между FIFO и оптимальным алгоритмом в том, что один смотрит назад, а другой вперед. Если использовать прошлое, для аппроксимации будущего, имеет смысл замещать страницу, которая не использовалась в течение долгого времени. Такой подход называется least recently used (LRU) алгоритм.

LRU часто используется и считается хорошим. Основная проблема - реализация.

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

Причем он должен обновляться при каждой ссылке. Много времени нужно на поиск страниц в списке. Есть вариант реализации со специальным устройством. Например, - иметь 64-битный указатель, который автоматически увеличивается на 1 после каждой инструкции и в таблице страниц иметь соответствующее поле, в которое заносится значение указателя при каждой ссылке на страницу. При возникновении page fault'а выгружается страница с наименьшим указателем.

Как оптимальный алгоритм, так и LRU не страдают от аномалии Белейди. Существует класс алгоритмов, называемых стековыми (stack) алгоритмами, которые не проявляют аномалии Белейди. Это алгоритмы, для которых множество страниц в памяти для n кадров всегда подмножество страниц для n+1 кадра. LRU таковым является.

Заметим, что никакая реализация LRU неприемлема без специального оборудования помимо стандартных регистров. Если, например, задействовать прерывание для модификации полей, то это будет замедлять ссылку к памяти в 10 раз.

10.3.4 Выталкивание редко используемой страницы. NFU (Not Frequently Used) алгоритм.

Программная реализация алгоритма, близкого к LRU.

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

Один из таких возможных алгоритмов - это алгоритм NFU.

Для него требуются программные счетчики, по одному на каждую страницу, которые сначала равны нулю. При каждом прерывании по времени (а не после каждой инструкции) операционная система сканирует все страницы в памяти и у каждой страницы с установленным флагом обращения увеличивает на единицу значение счетчика, а флаг обращения сбрасывает.

Таким образом, кандидатом на освобождение оказывается страница с наименьшим значением счетчика, как страница, к которой реже всего обращались. Главным недостатком алгоритма NFU является то, что он никогда ничего не забывает. Например, страница, к которой очень много обращались некоторое время, а потом обращаться перестали, все равно не будет удалена из памяти, потому что ее счетчик содержит большую величину. Например, в многопроходных компиляторах страницы, которые активно использовались во время 1-го прохода, могут надолго сохранить большие значения счетчика, мешая загрузке полезных в дальнейшем страниц.

К счастью, возможна небольшая модификация алгоритма, которая реализует "забывание". Достаточно, чтобы при каждом прерывании по времени содержимое каждого счетчика сдвигалось вправо на 1 бит, а уже затем производилось бы его увеличение для страниц с установленным флагом обращения (рис. 3-27 Таненбаум).

Другим, уже не так просто устранимым недостатком алгоритма является длительность процесса сканирования таблиц страниц.

10.3.5 Другие алгоритмы

Для полноты картины можно упомянуть еще несколько алгоритмов.

Например, алгоритм Second-Chance - модификации FIFO, которая позволяет избежать потери часто используемых страниц - анализ бита r (reference) для самой старой страницы. Если бит 1, то страница в отличие от FIFO не выталкивается, а очищается бит и страница становится в конец очереди. Если на все страницы ссылались, он превращается в FIFO. Данный алгоритм использовался в BSD Unix.

В компьютере Макинтош использован алгоритм NRU(Not Recently-Used), где страница жертва выбирается на основе анализа битов модификации и ссылки.

Имеется также и много других алгоритмов замещения. Объем этого курса не позволяет рассмотреть их подробно. Подробное описание различных алгоритмов замещения имеется в монографиях Дейтела, Цикритиса, Таненбаума и др.

10.4. Thrashing. Свойство локальности. Модель рабочего множества.

Что делать, если в распоряжении процесса имеется недостаточное число кадров? Следует ли его приостановить с освобождением всех его кадров. Что нужно понимать под достаточным количеством кадров?

Хотя теоретически возможно уменьшить число кадров процесса до минимума, существует какое-то число активно используемых страниц, без которого процесс часто генерирует faltы. Эта высокая частота страничных нарушений называется трешинг (trashing, иногда употребляется русский термин пробуксовка). Процесс находится в состоянии трешинга, если он больше времени занимается подкачкой страниц, нежели выполнением. Критическая ситуация такого рода возникает вне зависимости от конкретных алгоритмов замещения.

Рис. 10.2 Частота page fault'ов

Часто результатом трешинга является снижение производительности. Один из нежелательных сценариев развития событий может выглядеть следующим образом. При глобальном алгоритме замещения, процесс, которому не хватает кадров, начинает отбирать их у других процессов, а те в свою очередь начинают заниматься тем же. В результате все процессы попадают в очередь запросов к устройству вторичной памяти, а очередь процессов в состоянии готовности пустеет. Загрузка процессора снижается. Процессор видит это и увеличивает степень мультипрограммирования. Таким образом, пропускная способность системы падает из-за трешинга.

Эффект трешинга, возникающий при использовании глобальных алгоритмов, может быть ограничен за счет использования локальных алгоритмов замещения. Если даже один из процессов попал в трешинг, это не сказывается на других процессах. Однако он много времени проводит в очереди к устройству выгрузки, затрудняя подкачку страниц остальных процессов.

Критическая ситуация типа трешинга возникает вне зависимости от конкретных алгоритмов замещения. Единственным алгоритмом, теоретически гарантирующим отсутствие thrashing, является рассмотренный выше не реализуемый на практике оптимальный алгоритм.

Итак, трешинг - высокая частота страничных нарушений. Необходимо ее контролировать. Когда она высока, процесс нуждается в кадрах. Можно, устанавливая желаемую частоту fault'ов, регулировать размер процесса, добавляя или отнимая у него кадры. Как и в случае с рабочим множеством (см. ниже) может оказаться целесообразным придержать процесс, освободив его кадры. Освободившиеся кадры выделяются другим процессам с высокой частотой fault'ов.

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

Концепция локальности

Суть концепции локальности изложена во введении. Изучая свойства локальности в пейджинговых системах, Деннинг (1968) сформулировал теорию рабочего множества программ (см. ниже).

Модель локальности состоит в том, что когда процесс выполняется, он двигается от одной локальности к другой. Локальность - набор страниц, которые активно используются вместе. Программа обычно состоит из нескольких различных локальностей, которые могут перекрываться. Например, когда вызвана процедура, она определяет новую локальность, состоящую из инструкций процедуры, ее локальных переменных, и множества глобальных переменных. После ее завершения процесс оставляет эту локальность, но может вернуться к ней вновь. Таким образом, локальность определяется кодом и данными программы. Заметим, что модель локальности - принцип, положенный в основу работы кэша. Если бы доступ к любым типам данных был случайным, кэш был бы бесполезным.

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

Модель рабочего множества (Working Set)

В варианте чистого пейджинга процессы стартуют без необходимых страниц в памяти. Первая же машинная инструкции генерирует page fault. Другой page fault происходит при локализации глобальных переменных и тут же при выделении памяти для стека. После того как процесс собрал большинство страниц, page fault'ы далее редки. Эта все происходит в соответствии с выборкой по запросу (требованию) в отличие от выборки с упреждением. Конечно, легко написать тестовую программу, которая систематически работает с большим адресным пространством. К счастью, большинство процессов не ведут себя подобным образом, а проявляют свойство локальности. В течение любой фазы вычислений процесс работает с небольшим количеством страниц. Этот набор называется рабочим множеством. (Denning 1968,1980).

В некотором смысле предложенный Деннингом подход является практически реализуемой аппроксимацией оптимального алгоритма (см. также [28]. Принцип локальности ссылок (недоказуемый, но подтверждаемый на практике) состоит в том, что если в период времени (T-t, T) программа обращалась к страницам (P1, P2, ..., Pn), то при надлежащем выборе t с большой вероятностью эта программа будет обращаться к тем же страницам в период времени (T, T+t). Другими словами, принцип локальности утверждает, что если не слишком далеко заглядывать в будущее, то можно хорошо его прогнозировать исходя из прошлого. Набор страниц (P1, P2, ..., Pn) и есть рабочее множество программы (или, правильнее, соответствующего процесса) в момент времени T. Понятно, что с течением времени рабочий набор процесса может изменяться (как по составу страниц, так и по их числу).

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

Решение о размещении процессов в памяти должно, следовательно, базироваться на размере его рабочего множества. Для впервые инициируемых процессов это решение может быть принято эвристически. Во время работы размер рабочего множества процесса динамически меняется. Система должна уметь определять: расширяет процесс свое рабочее множество или перемещается на новое рабочее множество. Если память мала, чтобы содержать рабочее множество процесса, то page fault'ов много.

В системах с разделением времени процессы часто перемещаются на диск. Что делать, когда процесс возвращается в память. Опять ждать пока он соберет свое рабочее множество? Поэтому многие системы хранят информацию о рабочих множествах процессов и загружают их, даже если процесс еще не стартовал (стратегия выборки с упреждением).

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

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

В установившемся состоянии система следит за количеством исключительных ситуаций не присутствия страницы, вызываемых процессом. Если процесс генерирует page fault, и память не слишком заполнена, то система увеличит размер его рабочего множества. Если же процесс не вызывает исключительных ситуаций в течение некоторого времени, то система будет урезать его рабочее множество. При использовании таких алгоритмов система будет пытаться обеспечить наилучшую производительность для каждого процесса, не требуя никакой дополнительной настройки системы пользователем.

Итак, идея алгоритма подкачки Деннинга (иногда называемого алгоритмом рабочих наборов) состоит в том, что операционная система в каждый момент времени должна обеспечивать наличие в основной памяти текущих рабочих наборов всех процессов, которым разрешена конкуренция за доступ к процессору. Полная реализация алгоритма Деннинга практически гарантирует отсутствие thrashing. Алгоритм реализуем (известна, по меньшей мере, одна его полная реализация, которая, однако, потребовала специальной аппаратной поддержки). На практике применяются облегченные варианты алгоритмов подкачки, основанных на идее рабочего набора.

10.5 Демоны пейджинга

На практике алгоритмы, обеспечивающие поддержку системы в состоянии отсутствия трешинга, реализованы при помощи фоновых процессов (их часто называют демонами или сервисами), которые периодически просыпаются, и инспектируют состояние памяти. Например, если мало свободных кадров, они могут сменить стратегию замещения. Их задача поддерживать систему в состоянии наилучшей производительности.

Примером процесса такого рода может быть фоновый процесс-stealer (сборщик страниц), реализующий облегченный вариант алгоритма откачки, основанный на использовании рабочего набора и применяемый во многих клонах ОС Unix (см., например, Бах).

Основная идея заключается в оценке рабочего набора процесса на основе использования аппаратно (а в некоторых реализациях - программно) устанавливаемых признаков обращения к страницам основной памяти. Напомним, что анализ признаков обращения и модификации страниц позволяет выделить наименее используемые страницы. Системный процесс-stealer производит откачку страниц не входящих в рабочие наборы процессов. Он начинает работать, когда количество страниц в списке свободных страниц достигает установленного нижнего порога. Если, после генерации процессом page fault'а выясняется, что список свободных страниц не пуст, то из него выбирается вакантная страница, которая после подкачки подключается к виртуальной памяти процесса.

Но если возникает требование страницы в условиях, когда список свободных страниц пуст, то начинает работать механизм свопинга, поскольку простое отнятие страницы у любого процесса (включая тот, который затребовал бы страницу) потенциально вело бы к ситуации thrashing, и разрушало бы рабочий набор некоторого процесса. Любой процесс, затребовавший страницу не из своего текущего рабочего набора, становится в очередь на свопинг, в расчете на то, что после завершения свопинга одного из процессов свободной памяти уже может быть достаточно.

Другим примером может быть подсистема управления памятью Windows 2000, которая включает такие нити исполнения как:

- Working set manager, которая является менеджером балансного набора. Эта нить вызывается раз в секунду или тогда, когда свободная память опускается ниже определенного предела. Она ответственна за суммарную политику управления памятью, и поддержку рабочих множеств.
- Modified page writer - записывает модифицированные страницы в соответствующие файлы выгрузки. Эта нить пробуждается, когда размер списка модифицированных страниц нуждается в уменьшении,

и ряд других

10.6 Аппаратно-независимая модель памяти процесса.

Реализация функций операционной системы, связанных с поддержкой памяти: ведение таблиц страниц, трансляция адреса, обработка страничных ошибок, управление ассоциативной памятью и др., тесно связана со структурами данных, обеспечивающими удобное представление адресного пространства процесса. Формат этих структур сильно зависит от аппаратуры и особенностей конкретной ОС.

Чаще всего виртуальная память процесса ОС разбивается на сегменты пяти различных типов: кода программы, данных, стека, разделяемый и сегмент файлов, отображаемых в память.

Сегмент программного кода содержит только команды. Сегмент программного кода не модифицируется в ходе выполнения процесса, обычно страницы данного сегмента имеют атрибут read-only. Следствием этого является возможность использования одного экземпляра кода для разных процессов.

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

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

Более подробно информация об адресных пространствах процессов изложена в [1,28].

10.6.1 Структуры данных, используемые для описания сегментной модели

Для описания сегментной организации процесса используются структуры, содержащие дескрипторы отдельных сегментов и связанные с аппаратно-зависимой структурой (например, таблицей сегментов), данные которой используются при отображении виртуальных адресов в физические. Дескриптор сегмента содержит индивидуальные характеристики сегмента, в том числе:

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

Имеется также несколько описательных структур на уровне страниц. Так для управления физической памятью поддерживается несколько списков страниц: свободных, модифицированных, не допускающих модификации и т.д., которые просматриваются для выбора нужной страницы в зависимости от ситуации. Дескриптор физической страницы также содержит копии признаков обращения и модификации страницы, вырабатываемых аппаратурой.

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

Загрузка исполняемого файла (системный вызов exec) осуществляется обычно через отображение (mapping) его частей (кода, данных) в соответствующие сегменты адресного пространства процесса. После установления отображения, система начинает генерировать page fault'ы, в первую очередь для сегментов кода, данных и стека, подкачивая с диска необходимую информацию.

Как уже говорилось, сегмент данных динамически меняет свой размер. Пользователь, осуществляя аллокацию (библиотечный вызов malloc) или освобождение (free) динамических данных, фактически изменяет границу динамически выделенных процессу данных через системный вызов brk, который модифицирует значение переменной brk из структуры данных процесса. В результате происходит выделение физической памяти, а соответствующие строки таблиц страниц получают осмысленные значения. Ведение списков занятых и свободных областей памяти в сегменте данных пользователя осуществляется на уровне системных библиотек. На практике, освобожденная пользователем память (библиотечный вызов free) системе не возвращается, а резервируется для обслуживания возможных дальнейших запросов пользовательского процесса на выделение памяти.

10.7 Отдельные аспекты функционирования менеджера памяти.

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

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

Факт наличия виртуальной памяти не означает, что ввод-вывод отсутствует. Они взаимодействуют. Например, процесс может запросить ввод в буфер и ожидать его завершения. Управление передастся другому процессу, который может сгенерировать page fault и, с отличной от нуля вероятностью, спровоцировать выгрузку той страницы, куда должен быть осуществлен ввод первым процессом. Хаос, особенно, если ввод-вывод реализован через отдельный процессор (DMA transfer).

Одно из решений данной проблемы - вводить данные в невытесняемый буфер в пространстве ядра. А затем копировать их в пользовательское пространство. Дополнительное копирование может иметь результатом нежелательный оверхед.

Второе решение - локализовать страницы в памяти, используя специальный бит локализации. Локализованная страница не подлежит замещению. Она делокализуется после завершения операции ввода-вывода.

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

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

Другим важным применением локализации является ее использование в системах мягкого реального времени. Рассмотрим процесс или нить реального времени. Вообще говоря, виртуальная память - антитеза вычислений реального времени, так как дает непредсказуемые задержки при подкачке страниц. Поэтому системы реального времени почти не используют виртуальной памяти. ОС Solaris2 поддерживает, как реальное время, так и разделение времени. Для решения проблемы fault'ов, Solaris2 разрешает процессам сообщать системе, какие страницы важны для процесса и локализовать их в памяти. В результате возможно выполнение процесса, реализующего задачу реального времени, содержащего локализованные страницы, где временные задержки страничной системы будут минимизированы.

Помимо системы локализации страниц есть и другие интересные проблемы, возникающие в процессе управления памятью. Так, например, бывает непросто осуществить повторное выполнение инструкции, вызвавшей page fault. Представляют интерес алгоритмы отложенного выделения памяти (копирование при записи и др.). Ограниченный объем данного курса не позволяет рассмотреть их более детально.

#### 10.8 Заключение

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

Предыдущая глава | Программа курса | Следующая глава