Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Билеты Системное прогр Сымбат.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
595.46 Кб
Скачать

Экзаменационный билет № 15

1. Страничная и сегментная адресация

2. Учет пользователей объектов ядра. Защита.

3. Кучи. Управление памятью кучи.

Кучи

Win32 поддерживает области памяти в виде куч (heaps). Процесс может

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

Часто достаточно одной кучи, но по приведенным ниже причинам

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

функции библиотеки С для управления памятью (malloc, free, calloc,

realloc).

Кучи являются объектами Win32, поэтому они имеют дескрипторы.

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

HANDLE GetProcessHeap (VOID)

Возвращаемое значение: дескриптор кучи процесса; NULL в случае неудачи.

Заметим, что в данном случае для указания на ошибку возвращается

значение NULL, а не INVALID_HANDLE_VALUE, как в функции CreateFile.

Программа также может создавать особые кучи. Это относится к тем

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

структур данных. Ниже описаны преимущества отдельных куч.

• Справедливость распределения. Ни один поток не может получить

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

• Многопоточное быстродействие. Благодаря предоставлению каждому

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

• Эффективность резервирования. Резервирование элементов данных

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

Также снижается фрагментация памяти. Кроме того, выделение каждому

потоку отдельной кучи упрощает синхронизацию, что дает дополнительный

выигрыш.

• Эффективность освобождения памяти. Вся куча и все структуры данных,

размещенные в ней, могут быть освобождены одним вызовом функции. При

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

• Эффективная локализация ссылок. Расположение структуры данных в

маленькой куче гарантирует, что ее элементы будут сосредоточены в

сравнительно небольшом количестве страниц, что потенциально сокращает

страничные ошибки при обработке структуры.

Ценность этих преимуществ изменяется в зависимости от приложения, и

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

библиотекой С. В любом случае, следующие две функции создают и

уничтожают кучи.

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

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

HANDLE HeapCreate ( DWORD flOptions,

SIZE_T dwInitialSize, SIZE_T dwMaximumSize);

Возвращаемое значение: дескриптор кучи или NULL в случае ошибки.

Два поля размера имеют тип SIZE _ T , а не DWORD . Тип SIZE _ T определен таким образом, что может быть 32-разрядным или 64-разрядным беззнаковым целым, в зависимости от флагов компилятора (_WIN32 или _WIN64). Тип SIZE _ T был введен для обеспечения возможности перехода к WIN64. Переменная flOptions является комбинацией двух флагов.

• HEAP_GENERATE_EXCEPTIONS— при этом значении неудачные

попытки выделения памяти вызывают исключения, которые будут обработаны структурным обработчиком исключений (Structured Exception Handler — SEH). Функция HeapCreate сама по себе не вызывает исключений; если этот флаг установлен, исключение вызывают при неудаче такие функции, как HeapAlloc.

• HEAP_NO_SERIALIZE — установка этого флага в некоторых случаях

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

• Если значение dwMaximumSize. не равно нулю, виртуальное адресное

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

• С другой стороны, если значение dwMaximumSize. — нуль, то куча

является возрастающей за пределы начального размера. Эта граница

определяется доступным виртуальным адресным пространством, часть

которого может быть предоставлена другим кучам, и пространством файла

подкачки.

Отметим, что кучи не имеют атрибутов безопасности, так как они

недоступны извне процесса.

Для уничтожения всей кучи используйте функцию HeapDestroy. Это

другое исключение из общего правила о том, что функция CloseHandle

применяется для удаления всех ненужных дескрипторов.

BOOL HeapDestroy (HANDLE hHeap);

Переменная hHeap должна указывать на кучу, созданную функцией

HeapCreate. Будьте осторожны, не уничтожьте кучу процесса (полученную

функцией GetProcessHeap). Уничтожение кучи освобождает пространство

виртуальной памяти и физическую память в файле подкачки. Разумеется,

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

больше не используются.

Уничтожение кучи — это также быстрый способ освободить структуру

данных без необходимости уничтожать каждый элемент отдельно, хотя

экземпляры объектов С++ таким образом не будут уничтожены, поскольку их

деструкторы не вызываются. Уничтожение кучи имеет ряд преимуществ.

1. Нет необходимости писать код для поэлементного обхода структуры.

2. Нет необходимости освобождать каждый отдельный элемент.

3. Система не тратит время на поддержку кучи с того момента, когда все

структуры данных освобождаются одним системным вызовом.

Управление памятью кучи

Выделение блоков памяти из кучи осуществляется путем указания

дескриптора кучи, размера блока и нескольких флагов.

LPVOID HeapAlloc ( HANDLE hHeap, DWORD dwFlags, SIZE_T

dwBytes)

Возвращаемое значение: указатель на выделенный блок памяти или NULL

при неудаче (кроме случаев,- когда задано генерирование исключений).

Параметры функции HeapAlloc

hHeap — дескриптор кучи, из которой должен быть выделен блок памяти.

Этот дескриптор должен быть получен функциями GetProcessHeap или

HeapCreate.

dwFlags представляет собой комбинацию трех флагов.

• HEAP_GENERATE_EXCEPTIONS И HEAP_NO_SERIALIZE имеют то же

значение, что и для функции HeapCreate. Первый флаг можно не указывать,

если он был установлен функцией HeapCreate для данной кучи. Второй флаг

используется при выделении памяти из кучи процесса.

• HEAP_ZERO_MEMORY указывает на то, что выделенная память будет

заполнена нулями; в противном случае содержимое памяти будет

неопределенным.

dwBytes — размер блока памяти, который должен быть выделен. Для

невозрастающих куч его предел составляет 0x7FFF8 (примерно 0,5 Мбайт).

Освобождение памяти из кучи выполняется элементарно.

BOOL HeapFree ( HANDLE hHeap, DWORD dwFlags,

LPVOID lpMem)

dwFlags должен быть равен нулю или константе HEAP_NO_SERIALIZE.

lpMem должен иметь значение, возвращенное функцией HeapAlloc или

HeapReAlloc,

а hHeap — это дескриптор кучи, из которой был выделен блок lpMem.

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

LPVOID HeapReAlloc ( HANDLE hHeap, DWORD dwFlags,

LPVOID lpMem, SIZE_T dwBytes)

Возвращаемое значение: указатель на выделенный заново блок. При неудаче возвращает NULL или вызывает исключение.

Параметры функции HeapReAlloc

Первый параметр hHeap упоминался выше. dwFlags указывает некоторые

важные управляющие флаги.

• HEAP_GENERATE_EXCEPTIONS и HEAP_NO_SERIAL_S1ZE, как

в предыдущих случаях.

• HEAP_ZERO_MEMORY — инициализируется только заново выделяемая

память (когда значение dwByte больше размера исходного блока).

Содержимое исходного блока не изменяется.

• HEAP_REALLOC_IN_PLACE_ONLY указывает на то, что блок не может

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

lpMem указывает на существующий блок в куче hHeap, который должен

быть выделен заново.

dwBytes — новый размер блока, который может быть больше или меньше

текущего.

Обычно возвращаемый указатель — тот же, что и значение lpMem. Если же

блок был перемещен (это разрешается, когда флаг

HEAP_REALLOC_IN_PLACE_ONLY опущен), то возвращенное значение

будет другим. Следите за обновлением всех ссылок на блок.

Размер выделенного блока определяется функцией НеарSize (на самом деле

имя этой функции должно быть BlockSize, так как она относится к блоку, а не к куче) с указанием дескриптора кучи и указателя на блок.

DWORD HeapSize ( HANDLE hHeap, DWORD dwFlags,

LPCVOID lpMem)

Возвращаемое значение: размер блока или нуль при неудаче.

Флаг HEAP_NO_SER IALIZE

В функциях HeapCreate, HeapAlloc и HeapReAlloc может быть указан

флаг HEAP_NO_SERIALIZE. Его применение позволяет получить небольшой

прирост быстродействия, производительности, так как функции не

предоставляют потокам взаимные исключения при доступе к куче. Применение

этого флага безопасно в немногих описанных ниже ситуациях.

• Программа не использует потоки, или, точнее, процесс имеет только один

поток.

• Каждый поток имеет собственную кучу или группу куч, к которым не

обращаются другие потоки.

• Программа имеет собственный механизм взаимных исключений для

предотвращения одновременного доступа к куче нескольких потоков, исполь-

зующих функции HeapAlloc и HeapReAlloc. Для тех же целей в Windows

2000/NT доступны функции HeapLock и HeapUnlock.

Флаг HEAP_GENERATE__EXCEPTIONS

Вызов исключений в случае неудачного выделения памяти делает

ненужными раздражающие проверки ошибок после каждого выделения. Более

того, исключение или обработчик завершения может очистить память, которая

была выделена. Возможны два кода исключения.

1. STATUS_NO_MEMORY указывает на то, что система не может создать блок

заданного размера. Причиной может быть фрагментация памяти, невозраста-

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

2. STATUS_ACCESS_VIOLATION указывает на то, что куча была повреждена.

Например, программа могла произвести запись в память за границами выделен-

ного блока.

Другие функции для работы с кучей

Функция HeapCompact делает попытку объединения, или дефрагментации,

смежных свободных блоков в куче; HeapValidate пробует определить

повреждение кучи; HeapWalk перебирает блоки в куче, а GetProcessHeap

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

Функции HeapLock и HeapUnlock позволяют потоку упорядочить доступ

к куче.

Резюме по управлению кучей

Обычная последовательность действий при работе с кучей такова.

1. Получить дескриптор кучи с помощью функций CreateHeap или

GetProcessHeap

2. Выделить блоки в куче, используя HeapAlloc.

3. По необходимости освободить все или некоторые блоки функцией

HeapFree.

4. Уничтожить кучу и закрыть дескриптор функцией HeapDestroy.