|
|||||||
АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
Устройство виртуальной памяти4-гигабайтное адресное пространство процесса экономно используется небольшими фрагментами. Программы и элементы данных разбросаны по адресному пространству блоками по 4 кб, выровненными по границам, кратным 4 кб. Каждый такой блок называется страницей (page) и содержит либо код, либо данные. Естественно, используемая страница занимает какой-то участок физической памяти. Микропроцессор фирмы Intel эффективно преобразует 32-разрядный виртуальный адрес в номер физической страницы и смещение внутри нее, пользуясь двухуровневыми таблицами 4-килобайтных страниц. Заметьте: отдельные страницы можно помечать либо как только для чтения, либо как для чтения и записи. Кроме того, у каждого процесса свой набор таблиц страниц. Регистр CR3 процессора содержит указатель на страницу каталога, поэтому при переключении с одного процесса на другой Windows просто обновляет этот регистр.
Запись таблицы страниц содержит бит присутствия в физической памяти (present), который сообщает, находится ли сейчас в физической памяти данная 4-килобайтная страница. При попытке обращения к странице, отсутствующей в памяти, инициируется прерывание, и Windows анализирует ситуацию, просматривая свои внутренние таблицы. Если ссылка на область памяти оказывается ошибочной, мы получим ненавистное сообщение об ошибке страницы (page fault), и программа завершится. В противном случае Windows считает в оперативную память нужную страницу из дискового файла и обновит таблицу страниц, записав в нее физический адрес и установив бит присутствия в физической памяти. Так работает виртуальная память в Win32. Диспетчер виртуальной памяти Windows, стремясь достичь максимума производительности, определяет моменты чтения и записи страниц. Если какой-то процесс не использовал страницу в течение определенного периода, а другому нужна память, страница выгружается из памяти, а вместо нее загружается страница нового процесса. Обычно программа об этом не уведомляется. Все процессы совместно используют один большой общесистемный страничный файл (swap file) (ранее он назывался файл подкачки), куда помещаются (при необходимости) все виды данных для чтения и записи и некоторые виды данных только для чтения. (Windows NT/200/ХР способна одновременно поддерживать несколько страничных файлов,) Windows определяет размер страничного файла в зависимости от объема ОЗУ и свободного дискового пространства, но есть способы тонкой настройки размера и физического расположения этого файла. Диспетчер виртуальной памяти, однако, использует не только страничный файл. Записывать в него страницы кода резона нет. Вместо этого Windows проецирует содержимое ЕХЕ- и DLL-модулей прямо в их дисковые файлы. Поскольку страницы кода помечены «только для чтения», необходимости в их записи обратно на диск не возникает. Если два процесса используют один и тот же ЕХЕ-файл, последний отображается на адресные пространства обоих процессов. Код и константы никогда не изменяются во время выполнения программы, поэтому можно проецировать файл на одну и ту же область физической памяти. Однако два процесса не могут совместно обращаться к глобальным данным. С ними Windows 95/98 и Windows NT/200/ХР поступают по-разному. Windows 95/98 проецируют в каждый процесс отдельную копию глобальных данных, а вот в Windows NT/200/XP оба процесса используют одну копию глобальных данных до тех пор, пока один из процессов не попытается что-либо записать в страницу. В этот момент страница копируется, а затем у каждого процесса появляется собственная копия, находящаяся по одинаковому виртуальному адресу.
Язык С++ предоставляет программисту большой выбор функций для работы с динамической памятью. К функциям работы с дин. памятью относятся: malloc, calloc, farcalloc, farmalloc, realloc, free, new, delete. Функции XXXalloc и new используется для выделения блока динамической памяти, а free и delete - для его освобождения. Рассмотрим особенности работы с функцией malloc. Она имеет следующий формат: void *malloc(size_t size). Функция возвращает указатель на блок динамической памяти, заданного размера. Поскольку функция возвращает указатель типа void, то требуется явное преобразование типа. Пример: char * str; int * count; str = (char *) malloc (5); // отводится 5*sizeof(char) байт count = (int *) malloc (10*sizeof(int)); // отводится 10*sizeof(int) байт free(str); // освобождение памяти free(count); Теоретически в приведенном выше примере запрашивается и расходуется 5 + 10*2 = 25 байт ОП. Внимание! Как правило, компилятор настроен на автоматическое выравнивание адреса по границе параграфа (16 байт). Поэтому в нашем примере будет выделен 1 параграф под str и 2 параграфа под count. Т.о. всего будет занято 48 байт памяти. Место, где будет выделяться блок памяти зависит от используемой модели памяти. Но в любом случае память выделяется из области дополнительной памяти (кучи). Если имеется свободный последовательный участок памяти, достаточный для размещения необходимого числа байт, то функция malloc возвращает указатель на такой блок, иначе возвращается NULL. Содержимое выделенного блока не меняется. При size = 0, malloc возвращает NULL. Перед завершением программы следует освободить вся запрошенную динамическую память с помощью функций освобождения. В паре с malloc используется функция free. Функция calloc. Аналогична функции malloc, но имеет несколько другой набор параметров: void *calloc(size_t nitems, size_t size). Nitems - количество отводимых блоков, каждый из которых имеет размер size. Пример: char * str; int * count; str = (char *) calloc (10, sizeof(char)); count = (int *) calloc (5, sizeof(int)); При выделении памяти каждый байт устанавливается в 0. Данная функция доступна только в 16-разрядных приложениях. Также как и предыдущая функция позволяет получить память в пределах 64 Кбайт (1 сегмента). Для получения памяти за пределами 64Кбайт используются модификации функций: farmalloc и farcalloc. Пример: char far *fptr; fptr = (char far *) farmalloc(10); В качестве аргумента должно выступать unsigned long. Память, отведенная функциями farmalloc и farcalloc освобождается функцией farfree. Иногда возникает ситуация, когда отведенной ранее памяти недостаточно для работы. В этом случае требуется перераспределение памяти. Для этого используется функция realloc: void *realloc(void *block, size_t size); Пытается обрезать или расширить ранее выделенный блок памяти *block до размера size байт. Если значение size равно 0, то память отведенная под *block освобождается. Если *block указывает на NULL, то функция работает как malloc. При необходимости, недостаточно памяти для размещения нового блока, начиная с адреса block, старое содержимое block копируется по новому адресу. Возвращает адрес нового блока, который может не совпадать со старым адресом block. При нехватке памяти возвращает NULL. Все рассмотренные функции могут выделять память размером не более не более 4Г в 32-х разрядных моделях памяти. При работе с динамической памятью следует иметь в виду, что в каждом выделенном блоке несколько байт отводится на служебную информацию. В отличие от функций работы с динамической памятью malloc, calloc и free, заимствованных в С++ из стандарта ANSI С для совместимости, новые операторы гибкого распределения памяти new и delete обладают дополнительными возможностями, перечисленными ниже. - Функции malloc и calloc возвращают пустой указатель, который в дальнейшем требуется приводить к заданному типу. Оператор new возвращает указатель на тип, для которого выделялась память, и дополнительных преобразований уже не требуется. - Операция new предполагает возможность использования совместно с библиотечной функцией set_new__handler, позволяющей пользователю определить свою собственную процедуру обработки ошибки при выделении памяти. - Операторы new и delete могут быть перегружены с тем, чтобы они, например, могли принимать дополни- тельные параметры или выполняли специфические действия с учетом конкретной ситуации работы с памятью. Операторы new и delete имеют две формы: - управление динамическим размещением в памяти единичного объекта; - динамическое размещение массива объектов. Синтаксис при работе с единичными объектами следующий: тип_объекта *имя = new тип_объекта; delete имя; При управлении жизненным циклом массива объектов синтаксис обоих операторов имеет вид: тип__объекта *имя = new тип_объекта[число]; delete[] имя; Здесь число в операторе new[] характеризует количество объектов типа тип_объекта, для которых производится выделение области памяти. В случае успешного резервирования памяти переменная-указатель имя ссылается на начало выделенной области. При удалении массива его размер указывать не нужно. Форма оператора delete должна обязательно соответствовать форме оператора new для данного объекта. Пример: int * p; p = new int; Можно запросить сразу линейный массив в динамической памяти: p = new int [10]; // резервируется место под 10 int. При резервировании места для двумерных массивов нужно отдельно резервировать место под указатели на начало строк и на сами строки: Пример: int ** d; int i; d = new int* [n]; // выделяем память под указатели // на строки for (i=0; i<n; i++) // выделяем память под указатели // на столбцы d[i]=new int [m]; Для доступа к элементам массива используется обычное обращение: // инициализация массива for (i=0; i<n; i++) for (j=0; j<m; j++) {d[i][j]=i+j;} При выходе из программы требуется освобождать память с помощью оператора delete: for (int i = 0; i < n; i++) delete[] d[i]; // освобождаются столбцы delete[] d; // освобождаются указатели на строки
Полный текст программы приведен в файле exampl4.cpp. К сожалению, стандартные средства работы с динамической памятью не предусматривают "сборку мусора", то есть автоматическое перемещение выделенных блоков в динамической памяти так, чтобы между ними не было неиспользуемых промежутков. Поэтому от программиста требуется повышенное внимание к стратегии выделения и освобождения динамической памяти в своих программах. Иначе может получиться так, что требуемый блок памяти невозможно выделить, хотя суммарный объем неиспользуемой памяти допускает это. Все алгоритмы для управления областями памяти можно отнести к нескольким категориям, по типам объектов, с которыми они оперируют: 1. Объекты одного типа. Для выделения памяти под эти объекты нецелесообразно использовать malloc. Так как размер таких объектов постоянен, лучше использовать свой менеджер объектов, либо так называемый slab аллокатор. 2. Объекты определенного размера (например, страницы физической памяти). Для таких объектов наиболее подходят метод битовых масок или алгоритм "близнецов". 3. Объекты произвольного размера и типа (malloc). Для таких целей, как правило, применяется метод граничных маркеров. В современных системах используется двухуровневый подход. На нижнем уровне идет выделение страниц памяти, а на верхнем уровне происходит выделение кусков произвольной длины. Верхний уровень запрашивает память у системы с помощью диспетчера страниц. Существуют также системы, которые не нуждаются в нижнем уровне (диспетчере страниц), это, например, встроенные системы с ограниченными ресурсами, выполняющие статический набор процессов. В таких случаях диспетчер памяти может захватить при инициализации отведенное для него пространство и больше никогда не обращаться к диспетчеру страниц. Также это часто применяется в системах реального времени, где жертвуют предсказуемостью за счет удобства. Поиск по сайту: |
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Студалл.Орг (0.008 сек.) |