Управление виртуальной памятью в современных ос алгоритмы подкачки и вытеснения
Виртуальная память представляет собой совмещение оперативной памяти и временного хранилища файлов на жестком диске. В случае если памяти ОЗУ не достаточно, данные перемещаются во временное хранилище, называемое файлом подкачки. Для выполняющейся программы данный метод полностью прозрачен и не требует дополнительных усилий со стороны, однако реализация этого метода требует как аппаратной поддержки, так и поддержки со стороны операционной системы.
В системе с виртуальной памятью используемые программами адреса, называемые виртуальными адресами, транслируются в физические адреса в памяти компьютера. Трансляцию виртуальных адресов в физические выполняет аппаратное обеспечение, называемое блоком управления памятью (см. рисунок 1).
Содержание
Пробуксовка
Эта проблема возникает когда суммарный размер необходимых для работы приложений страниц превышает количество доступной реальной оперативной памяти, что вынуждает операционную систему постоянно выгружать и загружать страницы, затрачивая на это большое количество времени.
Привет, Хабрахабр!
В предыдущей статье я рассказал про vfork() и пообещал рассказать о реализации вызова fork() как с поддержкой MMU, так и без неё (последняя, само собой, со значительными ограничениями). Но прежде, чем перейти к подробностям, будет логичнее начать с устройства виртуальной памяти.
Конечно, многие слышали про MMU, страничные таблицы и TLB. К сожалению, материалы на эту тему обычно рассматривают аппаратную сторону этого механизма, упоминая механизмы ОС только в общих чертах. Я же хочу разобрать конкретную программную реализацию в проекте Embox. Это лишь один из возможных подходов, и он достаточно лёгок для понимания. Кроме того, это не музейный экспонат, и при желании можно залезть “под капот” ОС и попробовать что-нибудь поменять.
Любая программная система имеет логическую модель памяти. Самая простая из них — совпадающая с физической, когда все программы имеют прямой доступ ко всему адресному пространству.
При таком подходе программы имеют доступ ко всему адресному пространству, не только могут “мешать” друг другу, но и способны привести к сбою работы всей системы — для этого достаточно, например, затереть кусок памяти, в котором располагается код ОС. Кроме того, иногда физической памяти может просто не хватить для того, чтобы все нужные процессы могли работать одновременно. Виртуальная память — один из механизмов, позволяющих решить эти проблемы. В данной статье рассматривается работа с этим механизмом со стороны операционной системы на примере ОС Embox. Все функции и типы данных, упомянутые в статье, вы можете найти в исходном коде нашего проекта.
Будет приведён ряд листингов, и некоторые из них слишком громоздки для размещения в статье в оригинальном виде, поэтому по возможности они будут сокращены и адаптированы. Также в тексте будут возникать отсылки к функциям и структурам, не имеющим прямого отношения к тематике статьи. Для них будет дано краткое описание, а более полную информацию о реализации можно найти на вики проекта.
- Расширение реального адресного пространства. Часть виртуальной памяти может быть вытеснена на жёсткий диск, и это позволяет программам использовать больше оперативной памяти, чем есть на самом деле.
- Создание изолированных адресных пространств для различных процессов, что повышает безопасность системы, а также решает проблему привязанности программы к определённым адресам памяти.
- Задание различных свойств для разных участков участков памяти. Например, может существовать неизменяемый участок памяти, видный нескольким процессам.
Способы организации памяти
Page Fault
Page fault — это исключение, возникающее при обращении к странице, которая не загружена в физическую память — или потому, что она была вытеснена, или потому, что не была выделена.
В операционных системах общего назначения при обработке этого исключения происходит поиск нужной странице на внешнем носителе (жёстком диске, к примеру).
В нашей системе все страницы, к которым процесс имеет доступ, считаются присутствующими в оперативной памяти. Так, например, соответствующие сегменты .text, .data, .bss; куча; и так далее отображаются в таблицы при инициализации процесса. Данные, связанные с потоками (например, стэк), отображаются в таблицы процесса при создании потоков.
Выталкивание страниц во внешнюю память и их чтение в случае page fault не реализовано. С одной стороны, это лишает возможности использовать больше физической памяти, чем имеется на самом деле, а с другой — не является актуальной проблемой для встраиваемых систем. Нет никаких ограничений, делающих невозможной реализацию данного механизма, и при желании читатель может попробовать себя в этом деле :)
Для виртуальных страниц и для физических страниц, которые могут быть использованы при работе с виртуальной памятью, статически резервируется некоторое место в оперативной памяти. Тогда при выделении новых страниц и директорий они будут браться именно из этого места.
Исключением является набор указателей на PGD для каждого процесса (MMU-контексты процессов): этот массив хранится отдельно и используется при создании и разрушении процесса.
Выделение страниц
Итак, выделить физическую страницу можно с помощью vmem_alloc_page
Функция page_alloc() ищет участок памяти из N незанятых страниц и возвращает физический адрес начала этого участка, помечая его как занятый. В приведённом коде virt_page_allocator ссылается на участок памяти, резервированной для выделения физических страниц, а 1 — количество необходимых страниц.
Выделение таблиц
Тип таблицы (PGD, PMD, PTE) не имеет значения при аллокации. Более того, выделение таблиц производится также с помощью функции page_alloc(), только с другим аллокатором (virt_table_allocator).
После добавления страниц в соответствующие таблицы нужно уметь сопоставлять участки памяти с процессами, к которым они относятся. У нас в системе процесс представлен структурой task, содержащей всю необходимую информацию для работы ОС с процессом. Все физически доступные участки адресного пространства процесса записываются в специальный репозиторий: task_mmap. Он представляет из себя список дескрипторов этих участков (регионов), которые могут быть отображены на виртуальную память, если включена соответствующая поддержка.
brk — это самый большой из всех физических адресов репозитория, данное значение необходимо для ряда системных вызовов, которые не будут рассматриваться в данной статье.
ctx — это контекст задачи, использование которого обсуждалось в разделе “Виртуальный адрес”.
struct dlist_head — это указатель на начало двусвязного списка, организация которого аналогична организации Linux Linked List.
За каждый выделенный участок памяти отвечает структура marea
Поля данной структуры имеют говорящие имена: адреса начала и конца данного участка памяти, флаги региона памяти. Поле mmap_link нужно для поддержания двусвязного списка, о котором говорилось выше.
Ранее уже рассказывалось о том, как происходит выделение физических страниц, какие данные о виртуальной памяти относятся к задаче, и теперь всё готово для того, чтобы говорить о непосредственном отображении виртуальных участков памяти на физические.
Отображение виртуальных участков памяти на физическую память подразумевает внесение соответствующих изменений в иерархию страничных директорий.
Подразумевается, что некоторый участок физической памяти уже выделен. Для того, чтобы выделить соответствующие виртуальные страницы и привязать их к физическим, используется функция vmem_map_region()
В качестве параметров передаётся контекст задачи, адрес начала физического участка памяти, а также адрес начала виртуального участка. Переменная flags содержит флаги, которые будут установлены у соответствующих записей в PTE.
Основную работу на себя берёт do_map_region(). Она возвращает 0 при удачном выполнении и код ошибки — в ином случае. Если во время маппирования произошла ошибка, то часть страниц, которые успели выделиться, нужно откатить сделанные изменения с помощью функции vmem_unmap_region(), которая будет рассмотрена позднее.
Рассмотрим функцию do_map_region() подробнее.
Макросы GET_PTE и GET_PMD нужны для лучшей читаемости кода. Они делают следующее: если в таблице памяти нужный нам указатель не ссылается на существующую запись, нужно выделить её, если нет — то просто перейти по указателю к следующей записи.
В самом начале необходимо проверить, выровнены ли под размер страницы размер региона, физический и виртуальный адреса. После этого определяется PGD, соответствующая указанному контексту, и извлекаются сдвиги из виртуального адреса (более подробно это уже обсуждалось выше).
Затем последовательно перебираются виртуальные адреса, и в соответствующих записях PTE к ним привязывается нужный физический адрес. Если в таблицах отсутствуют какие-то записи, то они будут автоматически сгенерированы при вызове вышеупомянутых макросов GET_PTE и GET_PMD.
После того, как участок виртуальной памяти был отображён на физическую, рано или поздно её придётся освободить: либо в случае ошибки, либо в случае завершения работы процесса.
Изменения, которые при этом необходимо внести в структуру страничной иерархии памяти, производятся с помощью функции vmem_unmap_region().
Все параметры функции, кроме последнего, должны быть уже знакомы. free_pages отвечает за то, должны ли быть удалены страничные записи из таблиц.
try_free_pte, try_free_pmd, try_free_pgd — это вспомогательные функции. При удалении очередной страницы может выясниться, что директория, её содержащая, могла стать пустой, а значит, её нужно удалить из памяти.
нужны как раз для случая двухуровневой иерархии памяти.
Конечно, данной статьи не достаточно, чтобы с нуля организовать работу с MMU, но, я надеюсь, она хоть немного поможет погрузиться в OSDev тем, кому он кажется слишком сложным.
Стратегии управления виртуальной памятью, так же как и стратегии управления физической памятью, разделяются на три категории: стратегии вталкивания, стратегии размещения и стратегии выталкивания.
Целью стратегий вталкивания является определить, в какой момент следует переписать страницу или сегмент из вторичной памяти в первичную.
Целью стратегий размещения является определить, в какое место первичной памяти помещать поступающую страницу или сегмент.
Целью стратегий выталкивания является решить, какую страницу или сегмент следует удалить из первичной памяти, чтобы освободить место для помещения поступающей страницы или сегмента, если первичная память полностью занята.
Большинство стратегий управления виртуальной памятью базируется на концепции локальности, суть которой заключается в том, что распределение запросов процессов на обращение к памяти имеет, как правило, неравномерный характер с высокой степенью локальной концентрации.
Свойство локальности проявляется как во времени, так и в пространстве.
Локальность во времени означает, что к ячейкам памяти, к которым недавно производилось обращение, с большой вероятностью будет обращение в ближайшем будущем.
Локальность в пространстве означает, что обращения к памяти, как правило, концентрируются так, что в случае обращения к некоторой ячейке памяти с большой вероятностью можно ожидать обращение к близлежащим ячейкам.
Свойство локальности наблюдается не только в прикладных программах, но и в работе программ операционной системы. Свойство это скорее эмпирическое (наблюдаемое на практике), чем теоретически обоснованное. Локальность никак нельзя гарантировать, однако ее вероятность достаточно велика. Самым важным следствием локализации является то, что программа может эффективно работать, если в первичной памяти находится подмножество, включающее наиболее “популярные” ее страницы или сегменты.
Для оценивания эффективности стратегий управления памятью в операционных системах применяют показатель “пространство-время”, вычисляемый по формуле
где S - показатель “пространство-время”; V - объем первичной памяти, занимаемый процессом; T - длительность ожидания процессом подкачки необходимой страницы или сегмента.
Уменьшение значения показателя S за счет снижения периодов ожидания процессом нужных ему страниц или сегментов является важнейшей целью всех стратегий управления памятью.
Стратегии вталкивания (подкачки)
Для управления вталкиванием применяются следующие стратегии:
· вталкивание (подкачка) по запросу (по требованию);
· вталкивание (подкачка) с упреждением (опережением).
Вталкивание (подкачка) по запросу предполагает, что система ждет ссылки на страницу или сегмент от выполняющегося процесса и только после появления такой ссылки начинает переписывать данную страницу или сегмент в первичную память. Подкачка по запросу имеет положительные и отрицательные стороны.
К положительным сторонам относятся:
- гарантировано, что в первичную память будут переписываться только те страницы (сегменты), которые необходимы для работы процесса;
- накладные расходы на то, чтобы определить, какие страницы или сегменты следует передавать в первичную память, минимальны.
К недостаткам подкачки по запросу относится тот факт, что процесс в этом случае накапливает в первичной памяти требуемые ему страницы (сегменты) по одной. При появлении ссылки на каждую новую страницу (сегмент) процессу приходится ждать, когда эта страница (или сегмент) будет передана в первичную память. В зависимости от того, сколько страниц (сегментов) данного процесса уже находится в первичной памяти, эти периоды ожидания будут, как это следует из формулы (4.5), обходиться все более дорого, поскольку ожидающие процессы будут занимать все больший объем памяти.
Вталкивание (подкачка) с упреждением предполагает, что система пытается заблаговременно определить, к каким страницам или сегментам будет обращаться процесс. Если вероятность обращения высока и в первичной памяти имеется свободное место, то соответствующие страницы или сегменты будут переписываться в первичную память еще до того, как к ним будет явно производиться обращение. При правильном выборе страниц (сегментов) для упреждающей подкачки удается существенно сократить общее время выполнения данного процесса и уменьшить значение показателя “пространство-время”.
К недостаткам стратегии подкачки с упреждением можно отнести тот факт, что, согласно теории вычислимости, точно предсказать путь, по которому будет развиваться процесс, в общем случае невозможно. Поэтому вполне возможны ситуации, когда решения о выборе станиц (сегментов) для упреждающей подкачки будет в большинстве случаев приниматься неверно для одного или нескольких процессов, развивающихся в системе, что в свою очередь приведет к резкому снижению скорости работы этих процессов из-за увеличения времени ожидания необходимых им страниц или сегментов.
Стратегии размещения
В системах со страничной организацией виртуальной памяти решение о размещении вновь загружаемых страниц принимается достаточно просто: новая страница может быть помещена в любой свободный страничный кадр.
Для систем с сегментной организацией виртуальной памяти применяются такие же стратегии размещения, какие используются в системах распределения памяти переменными разделами (см. П.4.2), а именно:
· размещение с выбором первого подходящего свободного участка;
· размещение с выбором самого подходящего свободного участка;
· размещение с выбором наименее подходящего свободного участка.
Подробное описание действий для реализации перечисленных стратегий размещения приведено в п.4.2.3.
Стратегии выталкивания
В мультипрограммных системах вся первичная память бывает, как правило, занята. В этом случае программа управления памятью должна решать, какую страницу или какой сегмент следует удалить из первичной памяти, чтобы освободить место для поступающей страницы или сегмента. В настоящее время применяются следующие стратегии выталкивания (откачки) страниц (сегментов):
- выталкивание случайных страниц или сегментов;
- выталкивание первой пришедшей страницы или сегмента (FIFO);
- выталкивание дольше всего не использовавшихся страниц или сегментов (LRU);
- выталкивание наименее часто использовавшихся страниц или сегментов (LFU);
- выталкивание не использовавшихся в последнее время страниц или сегментов (NUR).
Стратегия выталкивания случайных страниц или сегментов является наиболее простой в реализации, обладает малыми издержками и не является дискриминационной по отношению к каким-либо процессам, работающим в системе. В соответствии с этой стратегией любые страницы или сегменты, находящиеся в первичной памяти, могут быть выбраны для выталкивания с равной вероятностью, в том числе даже следующая страница или сегмент, к которым будет производиться обращение (и которые, естественно, удалять из памяти наиболее нецелесообразно). Поскольку подобная стратегия, по сути, рассчитана на “слепое” везение, в реальных системах она применяется редко.
Стратегия выталкивания первой пришедшей страницы или сегмента (FIFO-стратегия) реализует принцип “первый пришел - первый ушел”. В этом случае в момент поступления каждой страницы (сегмента) в первичную память ей (ему) присваивается метка времени. Когда появляется необходимость удалить из первичной памяти какую-либо страницу (сегмент), выбирается та страница (сегмент), у которой метка времени имеет наименьшее значение. Аргументом в пользу такой стратегии выталкивания является довод, что у данной страницы уже были возможности “использовать свой шанс”, и пора дать подобные возможности другой странице. Однако стратегия FIFO с большой вероятностью будет приводить к удалению из первичной памяти активно используемых страниц (сегментов), поскольку тот факт, что страница (сегмент) находится в первичной памяти в течение длительного времени, вполне может означать, что эта страница или сегмент постоянно находится в работе.
Стратегия выталкивания дольше всего не использовавшихся страниц или сегментов (LRU-стратегия) предусматривает, что для выталкивания следует выбирать те страницы (сегменты), которые не использовались дольше других. Стратегия LRU требует, чтобы при каждом обращении к страницам (сегментам) их метки времени обновлялись. Это может быть сопряжено с существенными издержками, поэтому LRU-стратегия, несмотря на свою привлекательность, в современных операционных системах реализуется достаточно редко. Кроме того, при реализации LRU-стратегии может быть так, что страница (сегмент), к которой дольше всего не было обращений, в действительности станет следующей используемой страницей (сегментом), если программа к этому моменту очередной раз пройдет большой цикл, охватывающий несколько страниц или сегментов.
Стратегия выталкивания реже всего используемых страниц или сегментов (LFU-стратегия) является одной из наиболее близких к рассмотренной выше LRU-стратегии. В соответствии с LFU-стратегией из первичной памяти выталкиваются наименее часто (наименее интенсивно) использовавшиеся к данному времени страницы или сегменты. Здесь контролируется интенсивность использования страниц (сегментов). Для этого каждой странице (сегменту) назначается счетчик, значение которого увеличивается на единицу при каждом обращении к данной странице (сегменту). LFU-стратегия, будучи интуитивно оправданной, имеет те же недостатки, что и стратегия LRU: во-первых, велика вероятность того, что из первичной памяти будут удалены страницы или сегменты, которые потребуются процессам при следующем обращении к памяти и, во-вторых, ее реализация может быть сопряжена со значительными затратами на организацию контроля интенсивности использования страниц или сегментов.
Стратегия выталкивания не использовавшихся в последнее время страниц или сегментов (NUR-стратегия) также является близкой к стратегии LRU и характеризуется относительно небольшими издержками на свою реализацию. Согласно NUR-стратегии из первичной памяти выталкиваются те страницы (сегменты), к которым не было обращений в последнее время. В соответствии со свойством локальности во времени (см.п.4.4.1) к страницам (сегментам), не использовавшимся в последнее время, вряд ли будет обращение в ближайшем будущем, так что их можно заменить на вновь поступающие страницы.
Поскольку желательно заменять те страницы (сегменты), которые в период нахождения в основной памяти не изменялись, реализация NUR-стратегии предусматривает введение двух аппаратных бит-признаков на страницу (сегмент):
· бит-признак b0 обращения к странице (сегменту);
· бит-признак b1 модификации страницы (сегмента).
Первоначально все b0 и b1 устанавливаются в 0. При обращении к странице (сегменту) соответствующий бит-признак b0 устанавливается в 1. В случае изменения содержимого страницы (сегмента) соответствующий бит-признак b1 устанавливается в 1. NUR-стратегия предусматривает существование четырех групп страниц (сегментов), показанных в табл. 4.5.
Таблица 4.5.Группы страниц (сегментов)
Группа | b0 | b1 |
В первую очередь из первичной памяти выталкиваются страницы (сегменты), принадлежащие группам с меньшими номерами.
Учет времени, в течение которого к страницам (сегментам) не было обращений, осуществляется периодическим сбрасыванием в 0 всех битов-признаков, выполняемым операционной системой.
Практически любая стратегия выталкивания страниц (сегментов) не исключает опасности нерациональных решений. Это объясняется тем, операционная система не может точно прогнозировать будущее поведение любого из процессов, поступивших к ней на обработку.
Контрольные вопросы
1. Часто единственным достоинством виртуальной памяти называют возможность обеспечить для процесса объем виртуального адресного пространства, превышающий объем реальной памяти. Назовите другие достоинства виртуальной памяти.
2. В чем достоинства и недостатки преобразования виртуальных адресов в реальные во время выполнения программы? Какая часть работы по этому преобразованию выполняется аппаратным обеспечением, а какая - ОС?
3. Иногда считают, что виртуальная память может быть обеспечена только в системах с аппаратной поддержкой динамической трансляции адреса. Докажите, что это не так.
4. Почему при поиске свободной памяти стратегия "самый подходящий" оказывается хуже, чем "первый подходящий".
5. Сравните сегментную и страничную модели виртуальной памяти. Какая из них представляется Вам лучшей и почему?
6. Дополните приведенные в разделе 3.5. соображения по поводу выбора размера страницы.
7. Смоделируйте ситуацию применения дисциплины вытеснения FCFS, в которой увеличение числа реальных страниц приведет к увеличению числа страничных отказов.
8. Что такое кластерная подкачка страниц? Почему в современных ОС она становится все более популярной?
9. Каким образом ОС может определять, к каким страницам будут обращения в ближайшее время?
10. Большой размер виртуальной памяти процесса может приводить к тому, что даже таблица страниц не будет помещаться в реальной памяти. Какими путями решается эта проблема в современных ОС?
11. Каким образом снижение стоимости памяти влияет на дисциплины управления памятью?
12. Какие принципиальные изменения в концепции памяти может повлечь за собой увеличение разрядности адреса?
Лекция 6. Планирование процессов
6.1 Основные понятия планирования процессов
Планирование- обеспечение поочередного доступа процессов к одному процессору.
Планировщик - отвечающая за это часть операционной системы.
Алгоритм планирования - используемый алгоритм для планирования.
Ситуации, когда необходимо планирование:
- Когда создается процесс
- Когда процесс завершает работу
- Когда процесс блокируется на операции ввода/вывода, семафоре, и т.д.
- При прерывании ввода/вывода.
Алгоритм планирования без переключений(неприоритетный) - не требует прерывание по аппаратному таймеру, процесс останавливается только когда блокируется или завершает работу.
Алгоритм планирования с переключениями (приоритетный) - требует прерывание по аппаратному таймеру, процесс работает только отведенный период времени, после этого он приостанавливается по таймеру, чтобы передать управление планировщику.
Необходимость алгоритма планирования зависит от задач, для которых будет использоваться операционная система.
Основные три системы:
- Системы пакетной обработки - могут использовать неприоритетный и приоритетный алгоритм (например: для расчетных программ).
- Интерактивные системы - могут использовать только приоритетный алгоритм, нельзя допустить чтобы один процесс занял надолго процессор (например: сервер общего доступа или персональный компьютер).
- Системы реального времени - могут использовать неприоритетный и приоритетный алгоритм (например: система управления автомобилем).
Задачи алгоритмов планирования:
- Для всех систем
Справедливость - каждому процессу справедливую долю процессорного времени
Контроль над выполнением принятой политики
Баланс - поддержка занятости всех частей системы (например: чтобы были заняты процессор и устройства ввода/вывода) - Системы пакетной обработки
Пропускная способность - количество задач в час
Оборотное время - минимизация времени на ожидание обслуживания и обработку задач.
Использование процесса - чтобы процессор всегда был занят. - Интерактивные системы
Время отклика - быстрая реакция на запросы
Соразмерность - выполнение ожиданий пользователя (например: пользователь не готов к долгой загрузке системы) - Системы реального времени
Окончание работы к сроку - предотвращение потери данных
Предсказуемость - предотвращение деградации качества в мультимедийных системах (например: потерь качества звука должно быть меньше чем видео)
Планирование в системах пакетной обработки
6.2.1 "Первый пришел - первым обслужен" (FIFO - First In Fist Out)
Менеджер памяти - часть операционной системы, отвечающая за управление памятью.
Основные методы распределения памяти:
Без использования внешней памяти (например: HDD)
С использованием внешней памяти
6.2 Методы без использования внешней памяти
6.2.1 Однозадачная система без подкачки на диск
Память разделяется только между программой и операционной системой.
Схемы разделения памяти:
Схемы разделения памяти
Третий вариант используется в MS-DOS. Та часть, которая находится в ПЗУ, часто называется BIOS.
6.2.2 Распределение памяти с фиксированными разделами.
Память просто разделяется на несколько разделов (возможно, не равных). Процессы могут быть разными, поэтому каждому разделу необходим разный размер памяти.
Системы могут иметь:
общую очередь ко всем разделам
к каждому разделу отдельную очередь
Распределение памяти с фиксированными разделами
Недостаток системы многих очередей очевиден, когда большой раздел может быть свободным, а к маленькому выстроилась очередь.
Алгоритмы планирования в случае одной очереди:
выбирается задача, которая максимально займет раздел
Также может быть смешанная система.
6.2.3 Распределение памяти динамическими разделами
В такой системе сначала память свободна, потом идет динамическое распределение памяти.
Распределение памяти динамическими разделами.
Перемещаемые разделы
Это один из методов борьбы с фрагментацией. Но на него уходит много времени.
Рост разделов
Иногда процессу может понадобиться больше памяти, чем предполагалось изначально.
Настройка адресов и защита памяти
В предыдущих примерах мы можем увидеть две основные проблемы.
Настройка адресов или перемещение программ в памяти
Защита адресного пространства каждой программы
Решение обоих проблем заключается в оснащении машины специальными аппаратными регистрами.
Базовый (указывает начало адресного пространства программы)
Предельный (указывает конец адресного пространства программы)
6.3 Методы с использованием внешней памяти (свопинг и виртуальная память)
Так как памяти, как правило, не хватает. Для выполнения процессов часто приходится использовать диск.
Основные способы использования диска:
Свопинг (подкачка) - процесс целиком загружается в память для работы
Виртуальная память - процесс может быть частично загружен в память для работы
6.3.1 Свопинг (подкачка)
При нехватке памяти процессы могут быть выгружены на диск.
т.к. процесс С очень большой, процесс А был выгружен временно на диск,
после завершения процесса С он снова был загружен в память.
Как мы видим процесс А второй раз загрузился в другое адресное пространство, должны создаваться такие условия, которые не повлияют на работу процесса.
Свопер - планировщик, управляющий перемещением данных между памятью и диском.
Этот метод был основным для UNIX до версии 3BSD.
Управление памятью с помощью битовых массивов
Вся память разбивается на блоки (например, по 32бита), массив содержит 1 или 0 (занят или незанят).
Чтобы процессу в 32Кбита занять память, нужно набрать последовательность из 1000 свободных блоков.
Такой алгоритм займет много времени.
битовые массивы и списки
Управление памятью с помощью связных списков
Этот способ отслеживает списки занятых (между процессами) и свободных (процессы) фрагментов памяти.
Запись в списке указывает на:
занят (P) или незанят (H) фрагмент
адрес начала фрагмента
Четыре комбинации соседей для завершения процесса X
Алгоритмы выделения блока памяти:
первый подходящий участок.
следующий подходящий участок, стартует не сначала списка, а с того места на котором остановился в последний раз.
самый подходящий участок (медленнее, но лучше использует память).
самый неподходящий участок, расчет делается на то, что программа займет самый большой участок, а лишнее будет отделено в новый участок, и он будет достаточно большой для другой программы.
6.3.2 Виртуальная память
Основная идея заключается в разбиении программы на части, и в память эти части загружаются по очереди.
Программа при этом общается с виртуальной памятью, а не с физической.
Диспетчер памяти преобразует виртуальные адреса в физические.
Страничная организация памяти
Страничные блоки - единицы физической памяти.
Х - обозначает не отображаемую страницу в физической памяти.
Страничное прерывание - происходит, если процесс обратился к странице, которая не загружена в ОЗУ (т.е. Х). Процессор передается другому процессу, и параллельно страница загружается в память.
Таблица страниц - используется для хранения соответствия адресов виртуальной страницы и страничного блока.
Таблица может быть размещена:
в аппаратных регистрах (преимущество: более высокое быстродействие, недостаток - стоимость)
Типичная запись в таблице страниц
Присутствие/отсутствие - загружена или незагружена в память
Защита - виды доступа, например, чтение/запись.
Изменение - изменилась ли страница, если да то при выгрузке записывается на диск, если нет, просто уничтожается.
Обращение - было ли обращение к странице, если нет, то это лучший кандидат на освобождение памяти.
Информация о адресе страницы когда она хранится на диске, в таблице не размещается.
Для ускорения доступа к страницам в диспетчере памяти создают буфер быстрого преобразования адреса, в котором хранится информация о наиболее часто используемых страниц.
Страничная организация памяти используется, и в UNIX, и в Windows.
Хранение страничной памяти на диске
Статическая область свопинга
После запуска процесса он занимает определенную память, на диске сразу ему выделяется такое же пространство. Поэтому файл подкачки должен быть не меньше памяти. А в случае нехватки памяти даже больше. Как только процесс завершится, он освободит память и место на диске.
На диске всегда есть дубликат страницы, которая находится в памяти.
Этот механизм наиболее простой.
Статический и динамический методы организации свопинга.
Динамическая область свопинга
Предполагается не выделять страницам место на диске, а выделять только при выгрузке страницы, и как только страница вернется в память освобождать место на диске.
Этот механизм сложнее, так как процессы не привязаны к какому-то пространству на диске, и нужно хранить информацию (карту диска) о местоположении на диске каждой страницы.
Трансляция виртуального адреса в физический
Как уже писалось выше, при обращении к памяти трансляция адресов производится аппаратно, однако, явный доступ к физическим адресам может быть полезен в ряде случаев. Принцип поиска нужного участка памяти, конечно, такой же, как и в MMU.
Для того, чтобы получить из виртуального адреса физический, необходимо пройти по цепочке таблиц PGD, PMD и PTE. Функция vmem_translate() и производит эти шаги.
Сначала проверяется, есть ли в PGD указатель на директорию PMD. Если это так, то вычисляется адрес PMD, а затем аналогичным образом находится PTE. После выделения физического адреса страницы из PTE необходимо добавить смещение, и после этого будет получен искомый физический адрес.
Пояснения к коду функции.
mmu_paddr_t — это физический адрес страницы, назначение mmu_ctx_t уже обсуждалось выше в разделе “Виртуальный адрес”.
С помощью функции vmem_get_idx_from_vaddr() находятся сдвиги в таблицах PGD, PMD и PTE.
Размер страницы
В реальных (то есть не в учебных) системах используются страницы от 512 байт до 64 килобайт. Чаще всего размер страницы определяется архитектурой и является фиксированным для всей системы, например — 4 KiB.
С одной стороны, при меньшем размере страницы память меньше фрагментируется. Ведь наименьшая единица виртуальной памяти, которая может быть выделена процессу — это одна страница, а программам очень редко требуется целое число страниц. А значит, в последней странице, которую запросил процесс, скорее всего останется неиспользуемая память, которая, тем не менее, будет выделена, а значит — использована неэффективно.
С другой стороны, чем меньше размер страницы, тем больше размер страничных таблиц. Более того, при отгрузке на HDD и при чтении страниц с HDD быстрее получится записать несколько больших страниц, чем много маленьких такого же суммарного размера.
Отдельного внимания заслуживают так называемые большие страницы: huge pages и large pages [вики] .
Платформа | Размер обычной страницы | Размер страницы максимально возможного размера |
x86 | 4KB | 4MB |
x86_64 | 4KB | 1GB |
IA-64 | 4KB | 256MB |
PPC | 4KB | 16GB |
SPARC | 8KB | 2GB |
ARMv7 | 4KB | 16MB |
Действительно, при использовании таких страниц накладные расходы памяти повышаются. Тем не менее, прирост производительности программ в некоторых случаях может доходить до 10% [ссылка] , что объясняется меньшим размером страничных директорий и более эффективной работой TLB.
В дальнейшем речь пойдёт о страницах обычного размера.
Устройство Page Table Entry
В реализации проекта Embox тип mmu_pte_t — это указатель.
Каждая запись PTE должна ссылаться на некоторую физическую страницу, а каждая физическая страница должна быть адресована какой-то записью PTE. Таким образом, в mmu_pte_t незанятыми остаются MMU_PTE_SHIFT бит, которые можно использовать для сохранения состояния страницы. Конкретный адрес бита, отвечающего за тот или иной флаг, как и набор флагов в целом, зависит от архитектуры.
- MMU_PAGE_WRITABLE — Можно ли менять страницу
- MMU_PAGE_SUPERVISOR — Пространство супер-пользователя/пользователя
- MMU_PAGE_CACHEABLE — Нужно ли кэшировать
- MMU_PAGE_PRESENT — Используется ли данная запись директории
Можно установить сразу несколько флагов:
Здесь vmem_page_flags_t — 32-битное значение, и соответствующие флаги берутся из первых MMU_PTE_SHIFT бит.
Подкачка страниц
Практические все реализации виртуальной памяти делят виртуальное адресное пространство на страницы: блоки непрерывных виртуальных адресов. Размер страницы на современной системе обычно не меньше 4 килобайт, системы с большим диапазоном виртуальных адресов или количеством реальной оперативной памяти обычно используют более большие размеры страниц.
V=R режим
В OS/VS1 и похожих операционных системах, некоторые части системной памяти работают в режиме "Virtual-Real", часто сокращаемый до "V=R". В этом режиме каждый виртуальный адрес соответствует такому же реальному адресу. Этот режим используется для механизма прерываний, для супервизора страниц и таблицы страниц в старых операционных системах, и для приложений использующих не стандартное управление вводом/выводом.
Работа с Page Table Entry
Для работы с записей в таблице страниц, а так же с самими таблицами, есть ряд функций:
Эти функции возвращают 1, если у соответствующей структуры установлен бит MMU_PAGE_PRESENT
Закреплённые страницы
Операционные системы часто имеют области фиксированные области оперативной памяти, то есть область, никогда не выгружаемая во вторичную память. Это делается для улучшения производительности и упрощения реализации. Например, механизм прерываний полагается на массив указателей к свои обработчикам. Если страница, содержащая этот массив будет выгружена, или будет выгружен код, который должен быть вызван в случае исключения, то обработка исключения затянется во времени или станет невозможной.
Некоторые страницы требуется закрепить на короткое время, другие должны быть закреплены всегда:
- Код супервизора страниц и драйвера доступа к вторичной памяти (на котором находятся страницы), должен быть постоянно закреплён, иначе подкачка страниц не может не работать, так как необходимый код будет недоступен.
- Компоненты сильно зависимые от времени, должны быть закреплены, чтобы избежать задержек.
- Буферы данных, напрямую обращающиеся к периферийным устройствам, которые используют прямой доступ к памяти или каналы ввода/вывода должны находится в закреплённых страницах, во время операций с вводом/выводом, потому что устройства ожидают, что буферы расположены в реальной оперативной памяти. Передача информации с устройства не может быть остановлена в случае исключения ошибки страниц, и перезапущена позже.
Таблица страниц
Таблица страниц используется для чтобы транслировать адреса используемые приложением в физические адреса используемые устройствами. Оборудование, обеспечивающие такую трансляцию чаще всего называют устройством управления памятью. Каждая запись в таблицы страниц содержит флаг, указывающий находится ли соответствующая страница в реальной памяти или нет. Если страница в реальной памяти, в записи будет также хранится адрес реальной памяти, где эта страница записана. Если запись в таблице указывает, что на данный момент страница отсутствует в реальной оперативной памяти, аппаратная часть генерирует исключение ошибки страницы и вызывается страничный супервизор операционной системы.
В операционной системе может быть одна таблица страниц, по таблице страниц на каждое приложение или сегмент, дерево таблиц страниц для больших сегментов или комбинация перечисленного.
Страничный способ
Страничная память — способ организации памяти, при котором единицей отображения виртуальных адресов на физические является регион постоянного размера (т. н. страница). Типичный размер страницы — 4096 байт, для некоторых архитектур — до 128 КБ.
Основным достоинством страничного способа распределения памяти является минимально возможная фрагментация. Поскольку на каждую задачу может приходиться по одной незаполненной странице, то становится очевидно, что память можно использовать достаточно эффективно; этот метод организации виртуальной памяти был бы одним из самых лучших, если бы не два обстоятельства:
- Страничная трансляция виртуальной памяти требует существенных накладных расходов. В самом деле, таблицы страниц нужно тоже размещать в памяти. Кроме этого, эти таблицы нужно обрабатывать; именно с ними работает диспетчер памяти.
- Программы разбиваются на страницы случайно, без учета логических взаимосвязей, имеющихся в коде. Это приводит к тому, что межстраничные переходы, как правило, осуществляются чаще, нежели межсегментные, и к тому, что становится трудно организовать разделение программных модулей между выполняющимися процессами.
Решаемые задачи
- Поддержка изоляции процессов и защиты памяти путём создания своего собственного виртуального адресного пространства для каждого процесса
- Поддержка изоляции области ядра от кода пользовательского режима
- Поддержка памяти «только для чтения» и неисполняемой памяти
- Поддержка отгрузки давно не используемых страниц в область подкачки на диске (см. свопинг)
- Поддержка отображённых в память файлов, в том числе загрузочных модулей
- Поддержка разделяемой между процессами памяти, в том числе с копированием-по-записи для экономии физических страниц
- Поддержка системного вызова fork() в ОС семейства UNIX
Преимущества виртуальной памяти
- Освобождает программиста от необходимости вручную управлять загрузкой частей программы в память и согласовывать использование памяти с другими программами;
- Предоставляет программам больше памяти, чем физически установлено в системе;
- В многозадачных системах изолирует выполняющиеся программы друг от друга путём назначения им непересекающихся адресных пространств;
- Приложения исполняются в своём адресном пространстве и не мешают друг другу;
- Повышается безопасность за счёт защиты памяти.
Аппаратная поддержка
Обращение к памяти хорошо описанно в этой хабростатье. Происходит оно следующим образом:
Процессор подаёт на вход MMU виртуальный адрес
Если MMU выключено или если виртуальный адрес попал в нетранслируемую область, то физический адрес просто приравнивается к виртуальному
Если MMU включено и виртуальный адрес попал в транслируемую область, производится трансляция адреса, то есть замена номера виртуальной страницы на номер соответствующей ей физической страницы (смещение внутри страницы одинаковое):
Если запись с нужным номером виртуальной страницы есть в TLB [Translation Lookaside Buffer], то номер физической страницы берётся из нее же
Если нужной записи в TLB нет, то приходится искать ее в таблицах страниц, которые операционная система размещает в нетранслируемой области ОЗУ (чтобы не было промаха TLB при обработке предыдущего промаха). Поиск может быть реализован как аппаратно, так и программно — через обработчик исключения, называемого страничной ошибкой (page fault). Найденная запись добавляется в TLB, после чего команда, вызвавшая промах TLB, выполняется снова.
Таким образом, при обращении программы к тому или иному участку памяти трансляция адресов производится аппаратно. Программная часть работы с MMU — формирование таблиц страниц и работа с ними, распределение участков памяти, установка тех или иных флагов для страниц, а также обработка page fault, ошибки, которая происходит при отсутствии страницы в отображении.
В тексте статьи в основном будет рассматриваться трёхуровневая модель памяти, но это не является принципиальным ограничением: для получения модели с бóльшим количеством уровней можно действовать аналогичным образом, а особенности работы с меньшим количеством уровней (как, например, в архитектуре x86 — там всего два уровня) будут рассмотрены отдельно.
Виртуальный адрес
Page Global Directory (далее — PGD) — таблица (здесь и далее — то же самое, что директория) самого высокого уровня, каждая запись в ней — ссылка на Page Middle Directory (PMD), записи которой, в свою очередь, ссылаются на таблицу Page Table Entry (PTE). Записи в PTE ссылаются на реальные физические адреса, а также хранят флаги состояния страницы.
То есть, при трёхуровневой иерархии памяти виртуальный адрес будет выглядеть так:
Значения полей PGD, PMD и PTE — это индексы в соответствующих таблицах (то есть сдвиги от начала этих таблиц), а offset — это смещение адреса от начала страницы.
В зависимости от архитектуры и режима страничной адресации, количество битов, выделяемых для каждого из полей, может отличаться. Кроме того, сама страничная иерархия может иметь число уровней, отличное от трёх: например, на x86 нет PMD.
Для обеспечения переносимости мы задали границы этих полей с помощью констант: MMU_PGD_SHIFT, MMU_PMD_SHIFT, MMU_PTE_SHIFT, которые в приведённой выше схеме равны 24, 18 и 12 соответственно их определение дано в заголовочном файле src/include/hal/mmu.h. В дальнейшем будет рассматриваться именно этот пример.
На основании сдвигов PGD, PMD и PTE вычисляются соответствующие маски адресов.
Эти макросы даны в том же заголовочном файле.
Для работы с виртуальной таблицами виртуальной памяти в некоторой области памяти хранятся указатели на все PGD. При этом каждая задача хранит в себе контекст struct mmu_context, который, по сути, является индексом в этой таблице. Таким образом, к каждой задаче относится одна таблица PGD, которую можно определить с помощью mmu_get_root(ctx).
Программная поддержка
- Выделение физических страниц из некоторого зарезервированного участка памяти
- Внесение соответствующих изменений в таблицы виртуальной памяти
- Сопоставление участков виртуальной памяти с процессами, выделившими их
- Проецирование региона физической памяти на виртуальный адрес
Сегментный способ
Сегментная адресация памяти — схема логической адресации памяти компьютера в архитектуре x86. Линейный адрес конкретной ячейки памяти, который в некоторых режимах работы процессора будет совпадать с физическим адресом, делится на две части: сегмент и смещение. Сегментом называется условно выделенная область адресного пространства определённого размера, а смещением — адрес ячейки памяти относительно начала сегмента. Базой сегмента называется линейный адрес (адрес относительно всего объёма памяти), который указывает на начало сегмента в адресном пространстве. В результате получается сегментный (логический) адрес, который соответствует линейному адресу база сегмента+смещение и который выставляется процессором на шину адреса.
Достоинства сегментного способа
- Появляется возможность при загрузке программы на исполнение размещать ее в памяти не целиком, а «по мере необходимости». Поскольку в подавляющем большинстве случаев алгоритм, по которому работает код программы, является разветвленным, а не линейным, то в зависимости от исходных данных некоторые части программы, расположенные в самостоятельных сегментах, могут быть не задействованы, значит, их можно и не загружать в оперативную память.
- Некоторые программные модули могут быть разделяемыми. Эти программные модули являются сегментами, и в этом случае относительно легко организовать доступ к таким сегментам. Сегмент с разделяемым кодом располагается в памяти в единственном экземпляре, а в нескольких таблицах дескрипторов сегментов исполняющихся задач будут находиться указатели на такие разделяемые сегменты.
Недостатки сегментного способа
- Для получения доступа к искомой ячейке памяти необходимо потратить намного больше времени. Необходимо сначала найти и прочитать дескриптор сегмента, а уже потом, используя данные из него о местонахождении нужного сегмента, можем вычислить и конечный физический адрес. Для того чтобы уменьшить эти потери, используется кэширование — то есть те дескрипторы, с которыми мы имеем дело в данный момент, могут быть размещены в сверхоперативной памяти.
- Имеются большие потери памяти и процессорного времени на размещение и обработку дескрипторных таблиц. Ведь на каждую задачу необходимо иметь свою таблицу дескрипторов сегментов, а при определении физических адресов необходимо выполнять операции сложения.
Супервизор страниц
Это часть операционной системы, которая создаёт и управляет таблицами страниц. Если аппаратная часть генерирует исключение ошибки страницы, тогда супервизор страниц обращается к вторичному устройству памяти, извлекает необходимую страницу, обновляет таблицу страниц, чтобы отобразить новый физический адрес соответствующий виртуальному и запрашивает механизм трансляции повторить запрос.
Когда вся реальная оперативная память уже использована, супервизор страниц должен выгрузить какую-либо страницу во вторичную память, чтобы загрузить нужную. Супервизор использует алгоритм замещения страниц (например LRU), чтобы определить какую именно страницу можно выгрузить.
История появления
В 1940-ых и 50-ых все крупные программы должны были реализовывать логику управления памятью - оверлеи. Виртуальная предназначалась не только, чтобы расширить реальную оперативную память, но и облегчить использование памяти программистам. [Источник 1] .Чтобы использовать мультипрограммирование и многозадачность многие ранние системы разделяли память между множеством программ без использования виртуальной памяти (например ранние модели PDP-10 через регистры).
Концепция виртуальной памяти впервые была разработана немецким физиком Fritz-Rudolf Güntsch в Берлинском техническом университете в его докторской диссертации "Логическое построение цифровых компьютеров несколькими асинхронными вращающимеся барабанами и автоматическими высокоскоростными операциями с памятью" (англ. Logical Design of a Digital Computer with Multiple Asynchronous Rotating Drums and Automatic High Speed Memory Operation ). В диссертации описывалась машина с 6 блоками по 100 машинным слов основной памяти и адресное пространство из 1000 блоков по 100 машинных слов, с специальными аппаратными средствами автоматические перемещающие блоки между основной и вторичной памятью. Подкачка страниц впервые была реализована в Манчестерском университете как способ расширить реальную оперативную память компьютера Atlas совместив 16 тысяч слов основной памяти с дополнительными 96 тысячами слов вторичной памяти. Первый Atlas был введён в эксплуатацию в 1962-ом году, но рабочие прототипы подкачки страниц были реализованы к 1959-му году. [Источник 2] [Источник 3] В 1961-ом году, Burroughs Corporation самостоятельно выпустила первый коммерческий компьютер с виртуальной памятью - B5000, с сегментной адресацией памяти, но без подкачки страниц.
Перед тем как виртуальная память могла быть реализована в основным популярных операционных системах, много проблем должны были быть решены. Динамическая трансляция адресов требовала дорогого и трудно реализуемого специализированного оборудования (первые реализации несколько замедляли доступ к памяти). Так же существовало беспокойство, что новые общесистемные алгоритмы будут не так эффективно использовать вторичную память, как ранее используемые специальные алгоритмы реализуемые каждым приложением в отдельности. В 1969-ом году дебаты касающиеся использования виртуальной памяти в коммерческих компьютерах закончились, исследовательская команда IBM, возглавляемая David Sayre показала, что их система виртуальной памяти работает лучше, чем любая управляемая вручную система. Первый миникомпьютер использующий виртуальную память был Норвежский NORD-1, в течении 1970-ых годов, появились другие компьютеры реализующие виртуальную память (например компьютер VAX с системой VMS).
Виртуальная память была реализована в архитектуре x86 вместе с |защищённым режимом в процессоре Intel 80286, но его способ подкачки сегментов плохо масштабировался для сегментов большего размера. В процессор Intel 80386 была реализована подкачка страниц под существующем слоем сегментации, позволяя исключение подкачки страниц связывать с другими исключениями не вызывая двойного сбоя. Однако, загрузка дескриптора сегмента была дорогостоящей операции, в результате чего, разработчики операционных систем полагались только на подкачку страниц, а не на комбинацию сегментации и подкачки страниц. [Источник 4]
Читайте также: