Введение: от MS DOS к MS Windows.

            при переходе от MS DOS к MS Windows 3.x и далее к MS Windows 9x/Me и NT/2000/XP система управления памятью (СУП) претерпела существенные изменения. Чем проще операционная система (ОС) тем больше ее СУП отражает особенности аппаратной платформы, на которой выполняется ОС. MS DOS, ориентированная на работу с младшими моделями процессоров семейства Intel 80x86, поддерживала сегментированную модель памяти. В этой модели полный адрес ячейки ОЗУ формируется из двух частей: базового адреса (сегмент) и смещения от этой базы (собственно смещение). Такая модель соответствует стандартному (реальному) режиму работы процессоров Intel 80x86, в котором для хранения сегментной части адреса используются специальные 16-битные сегментные регистры. Смещение также хранится в 16-битных регистрах процессора. В такой архитектуре для доступа к ячейкам памяти, отстоящим от базы не более чем на 216 байт, достаточно менять только смещение, оставляя значение сегмента неизменным. Отсюда возникает "магическое число" - максимальный размер сегмента MS DOS - 64 Кб (216 байт).

            Языки программирования, поддерживающие создание системных приложений, для хранения адресов используют специальные переменные - указатели. В MS DOS возможно использование ближних (только смещение) и дальних (сегмент и смещение) указателей. Когда программа загружается в ОЗУ, часть адресов уже занята ядром ОС, драйверами устройств, другими программами (резидентными). Поэтому реальные адреса объектов программы формируются совместными действиями компилятора языка программирования и загрузчика ОС. В простейшем случае, компилятор формирует два сегмента: кода (содержит коды всех функций программы) и данных (глобальные/статические переменные и программный стек). Все идентификаторы программы после компиляции представлены в программе их адресами. При этом компилятор отсчитывает адреса от некоторого условного стартового или базового адреса. Загрузчик ОС определяет свободный адрес в ОЗУ и, используя его в качестве базового, настраивает все адреса программы. Глобальные и статические переменные существуют все время, пока выполняется программа (занимают фиксированный диапазон адресов в сегменте данных). Локальные переменные размещаются в программном стеке и, хотя стек - это динамически изменяющаяся структура данных, но все изменения стека определяются компилятором. Между адресом последней глобальной или статической переменной и верхушкой стека образуется область незанятых адресов. Эта область называется локальной "кучей" (local heap). Все свободные адреса за программой образуют глобальную кучу (global heap). Когда говорят о динамическом распределении памяти, то подразумевают возможность захвата, использования и освобождения дополнительных блоков памяти из глобальной или локальной кучи во время выполнения программы. В Си для динамического распределения памяти используют функции стандартной библиотеки: malloc(), calloc(), realloc(), free(). В C++ - операции new и delete.

            MS Windows 3.x переводит процессор в защищенный режим работы, с иной, более сложной схемой обращения к памяти. В этом режиме память распределяется страницами по 4 Кб каждая. На аппаратном уровне для адресации используются регистры дескрипторов страниц. Программа же работает не с физическими, а с виртуальными (логическими) адресами. ОС использует файл подкачки, позволяющий поддерживать диапазон виртуальных адресов больший, чем диапазон физически адресуемой памяти. При нехватке физической памяти часть страниц может быть выгружена в файл подкачки, а затем, при необходимости, вновь загружена в ОЗУ. При повторной загрузке, в общем случае, меняются как физический, так и виртуальный адреса страницы, поэтому программа не может полагаться на то, что указатель на динамически выделенный блок памяти содержит действительный адрес ячейки ОЗУ. ОС учитывает занятые блоки памяти, присваивая им уникальные целые положительные значения - дескрипторы. В связи с этим техника работы с динамически распределяемой памятью в MS Windows такова:

·        с помощью специальных функций MS Windows API (Application Programming Interface - набор функций, вызываемых приложением для запроса определенных сервисов у ОС), но не функций malloc(), calloc() или операции new, запрашивается блок памяти нужного размера из локальной (LockalAlloc()) или глобальной (GlobalAlloc()) кучи. При этом функция возвращает дескриптор блока;

·        всякий раз, когда необходим доступ к захваченному блоку, необходимо зафиксировать его в памяти (т.е. зафиксировать виртуальные адреса ячеек блока) с помощью другой функции (LocalLock() или GlobalLock()), которой передается полученный прежде дескриптор (дескрипторы не меняются при перемещении блоков памяти). Функция возвратит указатель, с которым теперь можно работать так же, как и в MS DOS;

·        завершив операции по доступу к блоку, необходимо отменить его фиксацию (функция LockalUnlock() или GlobalUnlock());

·        если блок больше не нужен - освободить память (LockalFree() или GlobalFree()).

Несмотря на столь существенные изменения, MS Windows 3.x все еще тесно связаны с понятием сегмента. Само разделение на локальную и глобальную кучи связано со все тем же 16-битным смещением или полным 32-битным адресом (сегмент:смещение). Более того, многие функции переключают процессор в реальный режим и являются по сути MS DOS функциями. Поэтому API MS Windows 3.x называют Win16 API.

Семейство ОС MS Windows 9x/Me и, тем более, MS Windows NT/2000/XP поддерживают не сегментированную, "плоскую" модель памяти. Весь (виртуальный) адрес вырождается в смещение, но это смещение 4-х байтовое (32 двоичных разряда). На текущий момент существует 64-разрядная версия MS Windows XP, в которой адрес задается 8-ю байтами. На аппаратном уровне, безусловно, используются регистры дескрипторов страниц, но приложение оперирует только с 4-х (или 8-и) байтовыми виртуальными адресами. В такой модели уже не может быть локальной кучи. Функции LocalAlloc(), GlobalAlloc() и т.д. поддерживаются, но, во-первых, работают они одинаково, а, во-вторых, лишь производят обращение к другой, базовой для Win32 API функции - VirtualAlloc(). теперь не нужно заботиться о фиксации блока в ОЗУ, т.к. виртуальные (но не физические) адреса ячеек блока не изменяются при любых его перемещениях, проводимых ОС. Учет блоков по-прежнему ведется с помощью дескрипторов, но, единожды получив указатель на блок, можно пользоваться им вплоть до удаления (освобождения) блока. Более того, VirtualAlloc() позволяет размещать блок по указанным (свободным) адресам (с учетом выравнивания на границу страницы) и задавать для него уровень доступа, т.е. обеспечивает уровень управления недоступный для функций динамического распределения памяти в MS DOS и MS Windows 3.x. С другой стороны это усложняет работу по динамическому распределению памяти. Иногда старая, простая дисциплина работы с некоторой "кучей" более удобна. И программист может использовать ее в программе. Для этого существует набор функций работы с "кучами" (HeapCreate(), HeapAlloc() и т.д.). Эти функции позволяют создать в программе несколько куч. Но, в любом случае, процесс окажется владельцем хотя бы одной кучи. Эту кучу не нужно создавать и нельзя уничтожить, но стандартные функции malloc(), calloc() и т.д. и операции new, delete распределяют память из этой кучи.

I. Имена, используемые в Windows для указателей и дескрипторов.

Application Programming Interface (API) включает в себя, помимо функций, также большой набор имен для различных типов данных - синонимов для стандартных типов и новых имен для новых составных типов. Каждая Windows-программа содержит файл windows.h "... поскольку он определяет очень широкий набор констант, структур, макросов и функций, которые составляют скелет всех Windows-программ". "Внутри windows.h вы найдете объявления ... большинства функций и типов среды Windows". "windows.h - это место, где объясняются многие таинства" [2]. Структура файла windows.h для новых (после 3.x) версий Windows изменилась радикально. Теперь это небольшой файл, в который помещаются директивы #include. Сами же определения разбросаны по многим .h-файлам. Поэтому для поиска нужной информации удобно использовать утилиты типа grep.exe из пакета Borland C++.

ЗАДАНИЕ 1. найдите в файлах windows.h, windef.h и т.п. объявления имен UINT, LPSTR, HANDLE, HINSTANCE, DWORD, HGLOBAL, LPVOID, BOOL, GLOBALHANDLE и запишите как, в конечном счете, соответствующие описания выражаются через стандартные типы языка Си. Попробуйте определить назначение именованной константы STRICT.

II. Динамическое распределение памяти.

            При запросе на динамическое выделение блока памяти указывается тип блока: фиксированный (fixed), перемещаемый (movable) или удаляемый (снимаемый, отбрасываемый) (discardable). Фиксированный блок памяти Windows может перемещать в физической памяти, но его виртуальный адрес остается неизменным. Приложение имеет дело только с виртуальными адресами, поэтому для работы с таким блоком можно использовать указатель, не интересуясь его дескриптором. Movable-блок может быть перемещен Windows как в физическом, так и в виртуальном адресном пространстве (чтобы избежать фрагментации виртуальной памяти). Неизменным останется лишь дескриптор блока. Поэтому при работе с перемещаемым блоком памяти нужно сначала его заблокировать (вызвав функцию GlobalLock см. ниже). Это приведет, во-первых, к фиксации виртуальных адресов ячеек блока, во-вторых, функция вернет указатель на начало блока и, в-третьих, к увеличению на единицу значения счетчика блокирований данного блока. Дальнейшая работа с блоком проводится обычным образом через полученный указатель. Когда текущая работа с блоком заканчивается его нужно снова разблокировать (вызвав функцию GlobalUnlock). Отбрасываемые блоки памяти (только Win16) отдают Windows полный контроль над блоком. При необходимости эти блоки могут уничтожаться самой ОС. Если затем они понадобятся в программе их нужно создавать вновь. В этом случае необходимо перед использованием блока проверять "жив" он или нет (если нет, то GlobalLock вернет NULL). Отбрасываемые блоки обязательно должны объявляться также и перемещаемыми.

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

1). Использование функций стандартной библиотеки Си.

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

            void *malloc(UINT size);

            void *calloc(UINT elem_count, UINT elem_size);

            void *realloc(void *old_ptr, UINT new_size);

            void free(void *ptr);

            Техника работы с динамической памятью в этом случае та же, что и при программировании в MS DOS. Отличия связаны лишь с вопросом о поддерживаемой модели памяти: сегментированная (ближние, дальние указатели) или с прямой 32-разрядной адресацией. Старые модели Windows (до Windows 95) поддерживали сегментированную модель памяти. В этом случае, как и для MS DOS, размер сегмента не может превышать 64K. При использовании прямой 32-разрядной адресации, программа может непосредственно обращаться к любой ячейке виртуальной памяти в пределах доступных 4 гигабайт (232). На самом деле программа для собственных нужд может использовать лишь половину этого адресного пространства. Остальную часть Windows "... использует в системных целях".

2). Использование операций С++.

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

3). Функции из WinAPI.

            Windows API содержит набор функций для работы как с фиксированными, так и перемещаемыми и удаляемыми (отбрасываемыми) блоками памяти. Некоторые функции приведены в следующей таблице, взятой из [2].

Имя функции

Описание

CopyMemory

Копирование блока памяти

FillMemory

Заполняет блок памяти заданными значениями

GlobalAlloc, LocalAlloc

Выделяет блок памяти заданного размера

GlobalDiscard, LocalDiscard

Отбрасывает выделенный блок памяти

GlobalFlags, LocalFlags

Возвращает флаги, соотв-е блоку памяти

GlobalFree, LocalFree

Освобождает выделенный блок памяти

GlobalHandle, LocalHandle

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

GlobalLock, LocalLock

Блокирует выделенный блок памяти

GlobalMemoryStatus

Возвращает размеры доступной физ. и вирт. памяти

GlobalRealloc, LocalRealloc

Изменяет размер выделенного блока памяти

GlobalSize, LocalSize

Возвращает размер выделенного блока памяти

GlobalUnlock, LocalUnlock

Отменяет блокировку блока памяти

MoveMemory

Перемещает блок памяти

VirtualAlloc

Выделяет виртуальную память

VirtualFree

Освобождает виртуальную память

ZeroMemory

Заполняет блок памяти нулями

В Win16 наиболее часто используются функции:

            HGLOBAL GlobalAlloc(UINT uFlags, DWORD dwBytes);

            HGLOBAL GlobalRealloc(HGLOBAL hMem, DWORD dwBytes, UINT uFlags);

            HGLOBAL GlobalFree(HGLOBAL hMem);

            GlobalAlloc позволяет захватить блок памяти любого типа. Этот тип задается параметром uFlags. Возможные значения объявлены в виде символических констант:

GMEM_FIXED - фиксированный блок

GMEM_MOVEABLE - перемещаемый

GMEM_DISCARDABLE - отбрасываемый (только Win16).

Вместе с этими константами может задаваться флаг GMEM_ZEROINIT, тогда выделенный блок памяти заполняется нулями. Для комбинаций констант определены свои имена:

GPTR как GMEM_FIXED | GMEM_ZEROINIT

GHND как GMEM_MOVEABLE | GMEM_ZEROINIT. Остальные возможные значения см. в файле GlobalAlloc.rtf.

Параметр dwBytes содержит размер запрашиваемого блока в байтах.

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

Пример 1.

int *ptr = (int *)GlobalAlloc(GPTR,4096);        // Фиксированный, заполненный нулями

// блок, размером 4K

ptr[100] += 10;

GlobalFree(ptr);

int *Ptr = (int *)malloc(4096);   // Фиксированный, не инициализированный блок,

// размером 4K

Ptr[100] = -1;

free(Ptr);

            Для работы с перемещаемыми (и отбрасываемыми) блоками нужно использовать функции GlobalLock и GlobalUnlock.

            LPVOID GlobalLock(GLOBALHANDLE hMem);

            BOOL GlobalUnlock(GLOBALHANDLE hMem);

Пример 2.

GLOBALHANDLE h = GlobalAlloc( GHND, 4096 );             // Перемещаемый, заполненный

// нулями блок

int *ptr = (int *)GlobalLock( h );

ptr[100] += 10;

GlobalUnlock( h );

GlobalFree( h );

            См. Help по этим функциям в MSDN и в приложении.

Для определения параметров виртуальной и физической памяти можно использовать функцию GlobalMemoryStatus:

            VOID GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer );

См. Help по структуре LPMEMORYSTATUS, выдаваемый MSDN, в приложении.

            Блоки памяти перемещаются из памяти в файл подкачки и наоборот фиксированными порциями - страницами. Размер страницы зависит от аппаратной платформы, на которой работает Windows. Для архитектуры Intel страницы имеют размер 4Kb. Узнать (в частности) размер страниц можно с помощью функции GetSystemInfo см. приложение.

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

            В Win32 каждый процесс может использовать не одну, но произвольное количество отдельных куч. Windows обязательно создает для процесса "стандартную" кучу размером 1Mb, который, при необходимости может увеличиваться. Память, захватываемая с помощью malloc, calloc, realloc или new принадлежит этой куче. Но любой поток процесса может создавать (HeapCreate) и освобождать (HeapDestroy) дополнительные кучи. Каждая куча (в том числе стандартная) управляется через собственный дескриптор. Для вновь создаваемой кучи дескриптор возвращается функцией HeapCreate, дескриптор стандартной кучи возвращает функция GetProcessHeap, кроме того (только в Windows NT 3.5 и старше), используя функцию GetProcessHeaps, можно получить массив дескрипторов для всех куч процесса.

            Зная дескриптор кучи, можно захватывать в ней блок памяти (HeapAlloc), изменять размер захваченного блока (HeapReAlloc) и освобождать блок (HeapFree), узнать размер блока, захваченного в куче и адресуемое заданным указателем (HeapSize). В Windows NT 3.5 и старше, кроме того, можно блокировать (HeapLock), разблокировать (HeapUnlock), проверять состояние (HeapValidate), дефрагментировать (HeapCompact) и просматривать информацию об отдельных блоках кучи (HeapWalk).

Описание функций см. в приложении.

4). Утилита heapwalk (только Windows 9x).

            Эта утилита позволяет проследить за тем, как приложение использует глобальную память. В главном окне этой программы отображается список строк, каждая из которых содержит информацию об одном блоке памяти: адрес (ADDRESS), дескриптор (HANDLE), размер в байтах (SIZE), счетчик фиксирования блока памяти (плюс к тому, если P - для блока запрещен страничный обмен, если L - блок зафиксирован) (LOCK), флаг (FLG) содержащий F - если блок фиксированный и D - если discardable, наличие локальной кучи (HEAP) - если есть, то Y, имя приложения, владеющего блоком (OWNER), тип объекта (сегмент кода, данных, ресурс и т.п.) (TYPE). В меню Walk пункт Walk Heap позволяет увидеть список блоков. Пункт Walk LRU List - список всех удаляемых объектов, GC(0) - дефрагментировать глобальную кучу и выделить блок памяти размером 0 байт, GC(-1) and Walk - удалить все удаляемые сегменты и посмотреть список объектов.

ЗАДАНИЕ 2. 1) Используйте функцию GlobalMemoryStatus для определения параметров конкретной системы, на которой Вы работаете. Запишите и объясните полученные результаты. 2) Определите, сколько реально памяти можно динамически захватить в Вашей вычислительной системе. Объясните результат. 3) Используйте heapwalk (только Windows 9x) для нахождения заблокированных участков памяти. Можно ли использовать heapwalk для освобождения заблокированных участков памяти?

ЗАДАНИЕ 3. (только для MS Windows NT). Используйте функцию HeapWalk для исследования всех блоков всех куч процесса. Создайте консольное приложение для отображения информации, выдаваемой HeapWalk. HepWalk возвращает нулевое значение, если возникла ошибка или просмотрены все блоки кучи. Чтобы получить информацию о первом блоке в куче необходимо перед вызовом HeapWalk присвоить полю lpData структуры PROCESS_HEAP_ENTRY значение NULL. Вызывая HepWalk снова, не меняя значений полей структуры PROCESS_HEAP_ENTRY, получаем информацию о следующем блоке и т.д. Чтобы не возникало коллизий между разными потоками, которые могут менять состояние кучи, прежде чем вызывать HeapWalk нужно заблокировать (зафиксировать) кучу, после возврата из HeapWalk - разблокировать.    

Приложение.

1. GlobalAlloc

Функция GlobalAlloc захватывает указанное число байт в куче. Поддерживается только для совместимости с Win16.

HGLOBAL GlobalAlloc(

  UINT uFlags,    // аттрибуты захватываемого блока

  DWORD dwBytes   // число байт в захватываемом блоке

);

Параметры:

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

            GMEM_FIXED - фиксированный блок, возвращаемое функцией GlobalAlloc значение - указатель на блок;

            GMEM_MOVEABLE - перемещаемый блок, возвращаемое функцией значение - дескриптор блока, чтобы получить указатель, нужно передать дескриптор функции GlobalLock; не может использоваться в комбинации с GMEM_FIXED;

            GPTR - комбинация GMEM_FIXED | GMEM_ZEROINIT;

            GHND - комбинация GMEM_MOVEABLE | GMEM_ZEROINIT;

            GMEM_ZEROINIT - все байты блока проинициализированы нулевыми значениями.

Если задать для uFlags нулевое значение, то это будет эквивалентно флагу GMEM_FIXED.

dwBytes - число байт в блоке.

Возвращаемые значения:

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

2. GlobalFree

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

HGLOBAL GlobalFree(

  HGLOBAL hMem   // дескриптор блока

);

Параметры:

hMem - дескриптор, возвращенный прежде функцией GlobalAlloc или GlobalReAlloc.

Возвращаемые значения:

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

3. GlobalReAlloc

Функция GlobalReAlloc изменяет размер или атрибуты блока памяти. Размер можно уменьшать или увеличивать.

Поддерживается только для совместимости с Win16.

HGLOBAL GlobalReAlloc(

  HGLOBAL hMem// дескриптор изменяемого блока

  DWORD dwBytes, // новый размер

  UINT uFlags    // новые аттрибуты

);

Параметры:

hMem - дескриптор, возвращенный прежде функцией GlobalAlloc или GlobalReAlloc.

dwBytes - новый размер блока в байтах. Если uFlags равен GMEM_MODIFY, этот параметр игнорируется.

uFlags - определяет, как перераспределять память. Если задан флаг GMEM_MODIFY, этот параметр определяет аттрибуты блока, и dwBytes игнорируется. В противном случае, этот параметр управляет переспределением памяти.

Флаг GMEM_MODIFY указывается в комбинации с флагом GMEM_MOVEABLE. Тогда блок объявленый прежде фиксированным становится перемещаемым.

Вместо GMEM_MODIFY можно указать GMEM_ZEROINIT. Тогда, если размер блока увеличивается, дополнительные байты будут обнулены.

Возвращаемые значения:

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

4. GlobalLock

Функция GlobalLock фиксирует блок в глобальной куче и возвращает указатель на его начало.

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

Поодерживается только для совместимости с Win16.

LPVOID GlobalLock(

  HGLOBAL hMem   // дескриптор захваченного прежде блока

);

Параметры:

hMem - дескриптор, возвращенный прежде функцией GlobalAlloc() или GlobalReAlloc().

Возвращаемые значения:

В случае успеха, указатель на первый байт блока, иначе NULL.

5. GlobalUnlock

Функция GlobalUnlock уменьшает значение счетчика фиксаций блока памяти на единицу, если блок был захвачен с атрибутом GMEM_MOVEABLE, блок фиксированный (флаг GMEM_FIXED), функция не делает ничего.

Поодерживается только для совместимости с Win16.

BOOL GlobalUnlock(

  HGLOBAL hMem   // дескриптор захваченного прежде блока

);

Параметры:

hMem - дескриптор, возвращенный прежде функцией GlobalAlloc() или GlobalReAlloc().

Возвращаемые значения:

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

6. MEMORYSTATUS

Структура MEMORYSTATUS предназначена для хранения информации о физической и о виртуальной памяти чичтемы. Функция GlobalMemoryStatus() сохраняет информацию в структуре MEMORYSTATUS.

typedef struct _MEMORYSTATUS {

    DWORD dwLength;        // sizeof(MEMORYSTATUS)

    DWORD dwMemoryLoad;    // процент используемой памяти

    DWORD dwTotalPhys;     // физической памяти (в байтах)

    DWORD dwAvailPhys;     // свободно физической памяти (в байтах)

    DWORD dwTotalPageFile; // байт в файле подкачки

    DWORD dwAvailPageFile; // свободно в файле подкачки (байт)

    DWORD dwTotalVirtual;  // адресуемое пользователькое пространство адресов (в байтах)

    DWORD dwAvailVirtual;  // свободно байт в пользовательском адресном пространстве

} MEMORYSTATUS, *LPMEMORYSTATUS;

7. GlobalMemoryStatus

Функция GlobalMemoryStatus позволяет получить информацию об использовании физической и виртуальной памяти.

VOID GlobalMemoryStatus(

  LPMEMORYSTATUS lpBuffer   // указатель на структуру memorystatus

);

Параметр:

lpBuffer - указатель на структуру MEMORYSTATUS, в которой функция GlobalMemoryStatus() сохраняет информацию о текущем состоянии памяти системы.

8. SYSTEM_INFO

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

typedef struct _SYSTEM_INFO {

    union {

        DWORD  dwOemId;       // для совместимости со старыми версиями Windows NT,

                                                // теперь не используется

        struct {

            WORD wProcessorArchitecture;         

// для Win9x - всегда PROCESSOR_ARCHITECTURE_INTEL

// для Windows NT возможны значения: PROCESSOR_ARCHITECTURE_INTEL,

// PROCESSOR_ARCHITECTURE_MIPS, PROCESSOR_ARCHITECTURE_ALPHA,

// PROCESSOR_ARCHITECTURE_PPC, PROCESSOR_ARCHITECTURE_UNKNOWN

            WORD wReserved;

        };

    };

    DWORD  dwPageSize;        // размер страницы памяти

    LPVOID lpMinimumApplicationAddress;  // указатель на наименьший доступный адрес

    LPVOID lpMaximumApplicationAddress; // указатель на наибольший доступный адрес

    DWORD  dwActiveProcessorMask;  // маска, представляющая множество доступных в

// системе процессоров: бит ноль - нулевой процессор: 0 - нет, 1 - есть; …; 31 бит - 31-й

// процессор

    DWORD  dwNumberOfProcessors;            // число процессоров в системе

    DWORD  dwProcessorType;                       // тип процессора:

// PROCESSOR_INTEL_386, PROCESSOR_INTEL_486,

// PROCESSOR_INTEL_PENTIUM. Только для NT еще два значения:

// PROCESSOR_MIPS_R4000, // PROCESSOR_ALPHA_21064

    DWORD  dwAllocationGranularity; // минимальный размер реально захватываемого

// блока, обычно 64 Кб.

    WORD  wProcessorLevel;  // В Win9x не используется, в NT: если

// wProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL, то возможны

// значения 3 (Intel 80386), 4 (Intel 80486), 5 (Pentium)

    WORD  wProcessorRevision;          // В Win9x не используется, в NT номер модели и

// stepping

} SYSTEM_INFO;

Более подробно см. MSDN.

9. GetSystemInfo

Функция GetSystemInfo возвращает информацию о вычислительной системе.

VOID GetSystemInfo(

  LPSYSTEM_INFO lpSystemInfo   // указатель на структуру SYSTEM_INFO

);

10. VirtualAlloc

Функция VirtualAlloc резервирует или распределяет диапазон страниц в виртуальном адресном пространстве вызывающего ее процесса. Захваченная с помощью этой функции память автоматически инициализируется нулями, если не установлен флаг MEM_RESET.

LPVOID VirtualAlloc(

  LPVOID lpAddress, // стартовый адрес резервируемого или распределяемого диапазона

  DWORD dwSize,     // размер блока

  DWORD flAllocationType, // тип распределения

  DWORD flProtect   // способ доступа к блоку

);

Параметры:

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

dwSize - размер блока в байтах. Реально выделяется память под целое число страниц. Если значение параметра lpAddress равно NULL, то dwSize плюс все байты до следующей страницы. Иначе, все страницы, содержащие адреса от lpAddress до (lpAddress+dwSize). Т.е. если, например, захватываем блок размером в страницу, но сос стартовым адресом смещенным от границы страницы на два байта, то реально будут захвачены две страницы.

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

Флаг

Значение

MEM_COMMIT

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

MEM_RESERVE

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

MEM_RESET

только Windows NT: Указывает, что страницы памяти из диапазона  от lpAddress до lpAddress +dwSize не будут читаться или записываться в файл подкачки. Фдаг MEM_RESET декларирует, что соделжимое страниц более не существенно и ОС может перезаписывать их содержимое.

MEM_TOP_DOWN

Распределяет память по наибольшим доступным адресам.

flProtect - указывает тип защиты доступа к блоку. Если память захватывается, могут быть указаны следующие флаги (если нужно, то совместно с флагами PAGE_GUARD и PAGE_NOCACHE):

Флаг

Значение

PAGE_READONLY

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

PAGE_READWRITE

Доступ на чтение и запись.

PAGE_EXECUTE

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

PAGE_EXECUTE_READ

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

PAGE_EXECUTE_READWRITE

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

PAGE_GUARD

Попытка записи или чтения для таких страниц вызывает исключение STATUS_GUARD_PAGE а затем сбрасывает GUARD флаг. Не может использоваться вместе с флагом PAGE_NOACCESS.

PAGE_NOACCESS

Запрещает доступ к распределенным страницам памяти. Любое обращение вызывает генерацию исключения "запрет доступа".

PAGE_NOCACHE

Позволяет не использовать кэширование распределенных страниц. Обычно не используется. Может быть полезен для драйверов устройств. Используется только вместе с другими флагами ограничения доступа (кроме  PAGE_NOACCESS).

Возвращаемые значения:

В случае успеха - стартовый адрес блока, в противном случае NULL.

11. VirtualFree

Функция VirtualFree освобождает зарезервированный или распределенный блок памяти в адресном пространстве текущего процесса.

BOOL VirtualFree(

  LPVOID lpAddress// адрес блока

  DWORD dwSize,      // размер блока

  DWORD dwFreeType   // тип операции

);

Параметры:

lpAddress - указатель на блок. Если параметр dwFreeType содержит флаг MEM_RELEASE, это должен быть стартовый адрес блока, возвращенный VirtualAlloc() при его резервировании.

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

dwFreeType - тип операции. Может быть одно из следующих значений (но не оба сразу):

Флаг

Значение

MEM_DECOMMIT

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

MEM_RELEASE

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

Возвращаемые значения:

В случае успеха возвращает не нулевое значение, иначе ноль.

12. GetProcessHeap

Функция GetProcessHeap возвращает дескриптор стандартной кучи процесса. Этот дескриптор может использоваться, затем, при вызовах функций HeapAlloc, HeapReAlloc, HeapFree, и HeapSize.

HANDLE GetProcessHeap(VOID)

Возвращаемые значения:

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

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

GetProcessHeaps

Функция GetProcessHeaps возвращает дескрипторы всех доступных процессу куч.

DWORD GetProcessHeaps(

  DWORD NumberOfHeaps// максимальное число дескрипторов в буфере

  PHANDLE ProcessHeaps  // указатель на буфер с полученными дескрипторами

);

Параметры:

NumberOfHeaps - максимальное количество дескрипторов куч, которое может быть сохранено в буфере, адресуемом параметром ProcessHeaps.

ProcessHeaps - указатель на буфер для дескрипторов куч.

Возвращаемые значения:

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

Замечание:

Не поддерживается в Win9x.

13. HeapCreate

Функция HeapCreate резервирут диапазон адресов в виртуальном адресном пространстве процесса и распределяет (захватывает) блок указанного начального размера.

HANDLE HeapCreate(

  DWORD flOptions,      // флаги распределения

  DWORD dwInitialSize// начальный размер блока

  DWORD dwMaximumSize   // максимальный размер кучи

);

Параметры:

flOptions - возможные атрибуты новой кучи:

Флаг

Значение

HEAP_GENERATE_EXCEPTIONS

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

HEAP_NO_SERIALIZE

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

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

dwMaximumSize - если значение этого параметра больше нуля, то он задает максимальный размер кучи (с учетом выравнивания на границу страницы). Кроме того, максимальный размер захватываемого в этой куче блока должен быть немного меньше чем 0x7FFF8 байт, даже если максимальный размер кучи больше этой величины. Если dwMaximumSize равен нулю, куча может менять свой максимальный размер. Ограничение - только общий размер доступной памяти. Запрос блока, размер которого больше чем 0x7FFF8 байт приводит к вызову VirtualAlloc для захвата такого блока памяти. Возвращаемые значения:

В случае успеха - дескриптор новой кучи. В случае неуспеха - NULL.

14. HeapDestroy

Функция HeapDestroy освобождает память занятую "кучей" и ее дескриптор становится недействительным.

BOOL HeapDestroy(

  HANDLE hHeap   // дескриптор кучи

);

Возвращаемые значения:

В случае успеха возвращает не нулевое значение, в противном случае - ноль.

15. HeapAlloc

Функция HeapAlloc распределяет фиксированный (не перемещаемый) блок памяти из кучи.

LPVOID HeapAlloc(

  HANDLE hHeap// дескриптор кучи

  DWORD dwFlags, // флаги распределения

  DWORD dwBytes  // размер лока в байтах

);

Параметры:

hHeap - дескриптор кучи, возвращенный прежде функцией HeapCreate или функцией GetProcessHeap.

dwFlags - флаги распределения, которые могут перекрывать флаги заданные при создании кучи:

Флаг

Значение

HEAP_GENERATE_EXCEPTIONS

При возникновении ошибки функция не будет возвращать NULL, но выбрасывается исключение, подобное "out-of-memory".

HEAP_NO_SERIALIZE

Не использовать "mutual exclusion" при доступе к куче нескольких потоков одновременно. Нельзя использовать для стандартной кучи. 

HEAP_ZERO_MEMORY

Инициализировать выделенный блок нулями.

dwBytes - число байт в блоке.

Возвращаемые значения:

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

Код

Значение

STATUS_NO_MEMORY

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

STATUS_ACCESS_VIOLATION

Повреждена куча или заданы неправильные параметры.

16. HeapReAlloc

Функция HeapReAlloc перераспределяет блок в куче, позволяя менять его размер или свойства. Новый блок будет фиксированным (not movable).

LPVOID HeapReAlloc(

  HANDLE hHeap// дескриптор кучи

  DWORD dwFlags, // флаги нового блока

  LPVOID lpMem// указатель на старый (подлежащий изменению) блок

  DWORD dwBytes  // новый размер блока

);

Возможно задание следующих флагов:

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

Возвращаемые значения:

В случае успеха возвращает указатель на новый блок, иначе, если не задан флаг HEAP_GENERATE_EXCEPTIONS, то возвращает NULL, а, если задан, генерируется исключение STATUS_NO_MEMORY или STATUS_ACCESS_VIOLATION.

17. HeapFree

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

BOOL HeapFree(

  HANDLE hHeap// дескриптор кучи

  DWORD dwFlags, // флаги

  LPVOID lpMem   // указатель на освобождаемый блок

);

Может быть указан только один флаг: HEAP_NO_SERIALIZE.

18. HeapSize

Функция HeapSize возвращает размер в байтах блока, распределенного прежде с помощью HeapAlloc или HeapRealloc.

DWORD HeapSize(

  HANDLE hHeap// дескриптор кучи

  DWORD dwFlags, // флаги

  LPCVOID lpMem  // указатель на блок

);

Может быть указан только один флаг: HEAP_NO_SERIALIZE.

19. HeapWalk

Функция HeapWalk позволяет получать информацию о блоках памяти в указанной куче.

BOOL HeapWalk(

  HANDLE hHeap// дескриптор кучи

  LPPROCESS_HEAP_ENTRY lpEntry      // указатель на структуру, в которой будет

// сохранена информация о блоке

);

Параметры:

hHeap - дескриптор кучи.

lpEntry - указатель на структуру PROCESS_HEAP_ENTRY, в которой будет сохранена информация о конкретном блоке.

Возвращаемые значения:

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

Замечания:

Чтобы начать просмотр блоков кучи, нужно вызвать HeapWalk задав для поля lpData структуры PROCESS_HEAP_ENTRY (см. ниже описание PROCESS_HEAP_ENTRY) значение NULL. Получим информацию о первом блоке кучи. Чтобы продолжить, нужно вызвать HeapWalk с тем же самым значением hHeap и lpEntry, не изменяя значения полей структуры PROCESS_HEAP_ENTRY. Этот процесс можно продолжать, пока функция не вернет FALSE, а GetLastError - ERROR_NO_MORE_ITEMS, что говорит о том, что все блоки в куче просмотрены.

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

Эта функция не поддерживается в Win9x.

20. PROCESS_HEAP_ENTRY

Структура PROCESS_HEAP_ENTRY содержит информацию об элементе (блоке) кучи. Используется функцией HeapWalk.

typedef struct _PROCESS_HEAP_ENTRY {

    PVOID lpData;

    DWORD cbData;

    BYTE cbOverhead;

    BYTE iRegionIndex;

    WORD wFlags;

    union {

        struct {

            HANDLE hMem;

            DWORD dwReserved[ 3 ];

        } Block;

        struct {

            DWORD dwCommittedSize;

            DWORD dwUnCommittedSize;

            LPVOID lpFirstBlock;

            LPVOID lpLastBlock;

        } Region;

    };

} PROCESS_HEAP_ENTRY;

Поля:

lpData - указатель на блок в куче.

cbData - размер блока в байтах.

cbOverhead - размер данных, в байтах, используемых системой для управления информацией об элементе кучи. Эти байты добавляются к cbData байтам данных блока.

iRegionIndex - дескриптор секции кучи, содержащей блок. Куча состоит из одной или большего числа секций виртуальной памяти, каждая из которых имеет свой уникальный индекс. Функция HeapAlloc время от времени использует функцию VirtualAlloc для распределения дополнительной памяти при возрастании размера кучи. Менеджер куч воспринимает эти дополнительные области как отдельные секции.

wFlags - множество битовых флагов, специфицирующих свойства блока кучи:

PROCESS_HEAP_REGION, PROCESS_HEAP_UNCOMMITTED_RANGE,

PROCESS_HEAP_ENTRY_BUSY, PROCESS_HEAP_ENTRY_MOVEABLE,

PROCESS_HEAP_ENTRY_DDESHARE. См. MSDN.

Block - эта структура имеет смысл, только если установлены оба флага PROCESS_HEAP_ENTRY_BUSY и PROCESS_HEAP_ENTRY_MOVEABLE в поле wFlags.

Поля структуры Block:

Поле

Описание

hMem

Дескриптор распределенного, перемещаемого блока.

dwReserved

Зарезервировано; не используется.

Region - эта структура имеет смысл, только если установлен флаг PROCESS_HEAP_REGION в поле wFlags.

Поля структуры Region:

Поле

Описание

dwCommittedSize

задает число байт в свободных блоках, занятых блоки или управляющих структурах кучи.

dwUnCommittedSize

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

lpFirstBlock

Указатель на первый доступный блок в этом диапазоне адресов кучи.

lpLastBlock

Указатель на первый не доступный блок в этом диапазоне адресов кучи.

Эта функция не поддерживается в Win9x.

 

 

На главную На главную
В начало В начало