Введение. Виртуальное адресное пространство процесса
Как уже говорилось, в ряде случаев система сама резервирует регионы в адресном пространстве процесса . Примерами таких регионов могут служить регион куча и регион стека потока.
Куча (heap) - зарезервированный регион размером в одну и более страниц, который рекомендуется использовать для хранения множества небольших порций данных. В отличие от функции VirtualAlloc правила гранулярности выделения памяти при этом соблюдать не обязательно.
Передачей памяти кучам, а также учетом свободной и занятой памяти в куче занимается специальный диспетчер куч ( heap manager ). Эта деятельность не документирована.
Стандартная куча процесса размером 1 Мб (эту величину можно изменить) резервируется в момент создания процесса . Обычно куча динамически меняет свой размер (флаг growable ). Стандартную кучу процесса используют не только приложения, но и некоторые Win32-функции. Для использования стандартной кучи необходимо получить ее описатель при помощи функции GetProcessHeap .
При желании процесс может создать дополнительные кучи при помощи функции HeapCreate (обратная операция HeapDestroy ). Прикладная программа выделяет память в куче с помощью функции HeapAlloc , а освобождает при помощи HeapFree .
Поскольку кучами могут пользоваться все потоки процесса, по умолчанию организуется синхронизация (флаг SERIALIZE ), которую, хотя это и не рекомендуется, для повышения быстродействия можно отменить. Задачу синхронизации доступа потоков к куче можно также решить, создавая каждому потоку свою собственную кучу.
Прогон программы выделения памяти в стандартной кучеВ приведенной программе происходит выделение массива памяти в стандартной куче процесса. Далее туда записывается текстовая строка, которая затем выводится на экран. Если возникает ситуация выхода за пределы выделенной памяти, которую легко смоделировать, увеличивая значение параметра Shift , - возникает ошибка исполнения.
В качестве самостоятельного упражнения можно рекомендовать наблюдение за наращиванием объема переданной в куче памяти при помощи счетчиков производительности. Объем виртуальной памяти процесса должен начать расти в случае выхода за пределы начального размера кучи (1 Мб) и при образовании дополнительных куч.
Регион стека потока. Сторожевые страницыДля поддержки функционирования стека потока также резервируется соответствующий регион. Стек - динамическая структура. Принято, чтобы стек увеличивал свой размер в сторону уменьшения адресов. Сколько страниц памяти потребуется стеку потока, заранее не известно. Поэтому в ОС Windows организована поэтапная, по мере необходимости, передача физической памяти стеку при помощи механизма так называемых "сторожевых" страниц ( guard page). Обращение к сторожевой странице имеет следствием уведомление системы об этом (исключительная ситуация 0x80000001 ), после чего флаг PAGE GUARD сбрасывается и со страницей можно работать как с обычной страницей преданной памяти. Сторожевая страница служит ловушкой для перехвата ссылок за ее пределы.
При создании потока для его стека резервируется регион размером 1 Мб (по умолчанию), и ему передается 2 страницы памяти (эти параметры могут быть изменены). Нижняя страница является сторожевой. Как только верхняя страница оказалась заполненной и произошло обращение к нижней странице, это замечается системой и региону передается еще одна страница, теперь уже она становится сторожевой. Вследствие такой тактики самая нижняя переданная региону стека страница всегда остается сторожевой и ее задача - просигнализировать системе о том, что объем переданной стеку памяти нужно увеличить.
Прогон программы, моделирующей обращение к сторожевым страницамВ приведенной программе происходит передача памяти региону и установка флага PAGE_GUARD для его страниц. Кроме того, используется структурная обработка исключений. В случае попытки записи текстовой строки на сторожевую страницу (в блоке try ) возникает исключительная ситуация exception 0x80000001 . При повторной попытке записи на эту страницу (блок except ) подобная ситуация уже не возникает.
Написание, компиляция и прогон программы, моделирующей рост стека при помощи механизма сторожевых страниц
В качестве самостоятельного упражнения рекомендуется написать программу, в которой сторожевая страница все время находится на границе фрагмента переданной памяти. В случае обращения к сторожевой странице объем переданной процессу памяти нужно увеличить, а сторожевую страницу переместить.
Регион файла, отображаемого в памятьТехника файлов, проецируемых в память (см. рис. 9.4), активно используется новейшими ОС. Она позволяет пользователю решать такие задачи, как работа с данными файла при помощи операций копирования и перемещения байтов в памяти или организация совместного доступа к областям памяти. Этот механизм также активно используется самой операционной системой, например, для загрузки в память исполняемых модулей, динамических библиотек и отображения файла в буфер кэша для осуществления операций стандартного ввода-вывода.
Отображение файла в память означает резервирование региона нужного размера и передача ему соответствующего объема физической памяти (передавать память здесь тоже можно поэтапно). Однако, в отличие от обычных регионов, выгрузка фрагментов оперативной памяти во внешнюю память будет при этом осуществляться не в системную область внешней памяти ( pagefile.sys ), а непосредственно в отображаемый файл. Отображение может быть выполнено на конкретный диапазон виртуальных адресов, если это не противоречит местоположению уже существующих регионов виртуальной памяти.
Для отображения файла в память используется функция CreateFileMapping , а для получения указателя на отображенную область - функция MapViewOfFile . Успешное выполнение обеих операций позволяет прикладной программе работать с этой областью как с любым другим фрагментом выделенной памяти, в частности, изменять ее содержимое. В связи с этим возникает проблема соответствия (когерентности) содержимого региона и файла на диске. Операционная система старается обеспечить когерентность, однако в распоряжении пользователя есть возможность в любой момент сбросить содержимое памяти на диск при помощи функции FlushViewOfFile .
Другой интересный момент связан с тем, что система рассматривает проецируемый в память файл как объект многоцелевого назначения. Поэтому отображение файла в память сопровождается созданием сопутствующего объекта ядра (в данном случае это объект-секция) с именем в пространстве объектов, по которому он может быть доступен другим процессам, счетчикам ссылок с атрибутами защиты. Как обычно ссылка на объект хранится в таблице описателей каждого процесса, имеющего к объекту доступ.
Прогон программы, демонстрирующей отображение файла в памятьПриведенная программа демонстрирует этапы создания файла, проецирования его в память, изменения его содержимого и отображения на диск.
Написание, компиляция и прогон программы, демонстрирующей различные аспекты проецирования файла в памятьНа основе предыдущей программы рекомендуется написать программу отображения файла в память с промежуточной выгрузкой файла на диск при помощи функции FlushViewOfFile . Рассмотрите различные варианты существования файла и его размеров до и после отображения.
Решение задачи совместного доступа к памяти будет приведено ниже.
Здесь мы временно прекратим изучение виртуальной памяти процесса. Основным итогом изложенного можно считать знакомство с возможностями ОС создавать в ней разнообразные регионы. В качестве самостоятельного упражнения можно рекомендовать анализ состояния виртуального адресного пространства при помощи функций VirtualQuery и VirtualQueryEx (см., например, [ Рихтер ] ). Теперь перейдем к рассмотрению других аспектов функционирования менеджера памяти: структуре физической памяти и особенностям трансляции адреса.
ЗаключениеСистема управления памятью является одной из наиболее важных в составе ОС. Традиционная схема предполагает связывание виртуального и физического адреса на стадии исполнения программы. Для управления виртуальным адресным пространством в нем принято организовывать сегменты (регионы), для описания которых используются структуры данных VAD ( virtual address descriptors). Для создания региона и передачи ему физической памяти можно использовать функцию VirtualAlloc . Описана техника использования таких регионов, как куча процесса, стек потока и регион файла, отображаемого в память .