На этом шаге мы рассмотрим функции, используемые для управления памятью.
На этом шаге мы разберем несколько функций, позволяющих динамически распределять и удалять блоки памяти.
Начнем с функции GlobalAlloc. Другая функция, LocalAlloc, полностью эквивалентна первой и сохранена только для совместимости со старыми приложениями. Функция имеет два аргумента. Первым аргументом является флаг, о значении которого будем говорить позже. Вторым аргументом является число необходимых байтов. Если функция выполнена успешно, то она возвращает адрес начала блока, который можно использовать в дальнейших операциях. Если же система не может выделить достаточно памяти, то функция возвращает 0.
Обычно значение флага принимают равным константе GMEM_FIXED, которая равна нулю. Это означает, что блок памяти - неперемещаемый. Неперемещаемость следует понимать в том смысле, что не будет меняться виртуальный адрес блока, тогда как адрес физической памяти, куда проецируется данный блок, может, разумеется, изменяться системой. Комбинация данного флага с флагом GMEM_ZEROINIT приводит к автоматическому заполнению выделенного блока нулями, что часто бывает весьма удобно. Изменить размер выделенного блока можно при помощи функции GlobalReAlloc. Первым аргументом данной функции является указатель на изменяемый блок, второй аргумент - размер нового блока, третий аргумент - флаг. Заметим, что данная функция может изменить свойства блока памяти, т.е., например, сделать его перемещаемым.
Обратимся теперь снова к флагам функции GlobalAlloc. Если ваша программа интенсивно работает с памятью, т.е. многократно выделяет и освобождает память, память может оказаться фрагментированной. Действительно - вы же запрещаете перемещать блоки. В этом случае можно использовать флаг GMEM_MOVEABLE. Выделив блок, вы можете в любой момент зафиксировать его при помощи функции GlobalLock, после этого спокойно работая с ним. С помощью функции GlobalUnlock можно в любой момент снять фиксацию, т.е. разрешить системе упорядочивать блоки. Надо иметь в виду, что при использовании флага GMEM_MOVEABLE возвращается не адрес, а дескриптор. Но как раз аргументом функции GlobalLock и является дескриптор. Сама же функция GlobalLock возвращает адрес.
Возможен и еще более экзотический подход с использованием флага GMEM_DISCARDABLE. Этот флаг используется совместно с GMEM_MOVEABLE. В этом случае блок может быть удален из памяти системой, если только вы его предварительно не зафиксировали. Если блок был удален системой, то функция GlobalLock возвратит 0 и вам придется снова выделять блок и загружать, если необходимо, данные.
Для удаления блока памяти используется функция GlobalFree. Причем в случае выделения фиксированного блока памяти, аргументом функции является адрес блока памяти, а в случае перемещаемого блока памяти - дескриптор. Для освобождения удаляемого блока памяти используйте функцию GlobalDiscard.
Особо нужно отметить функцию GlobalMemoryStatus, с помощью которой можно определить количество свободной памяти. Единственным параметром данной функции является указатель на структуру, содержащую информацию о памяти. Вот эта структура:
MEM STRUC DwLength DD ? DwMemoryLoad DD ? DwTotalPhys DD ? DwAvailPhys DD ? DwTotalPageFile DD ? DwAvailPageFile DD ? DwTotalVirtual DD ? DwAvailVirtual DD ? MEM ENDS
Здесь:
В следующем примере представлено простейшее применение функции GlobalAlloc.
.386P ;Плоская модель. .MODEL FLAT, STDCALL ;Константы. STD_OUTPUT_HANDLE equ -11 GENERIC_READ equ 80000000h OPEN_EXISTING equ 3 ;Прототипы внешних процедур. IFDEF MASM EXTERN GlobalFree@4:NEAR EXTERN GlobalAlloc@8:NEAR EXTERN GetFileSize@8:NEAR EXTERN CloseHandle@4:NEAR EXTERN CreateFileA@28:NEAR EXTERN ReadFile@20:NEAR EXTERN GetStdHandle@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetCommandLineA@0:NEAR ELSE LOCALS EXTERN GlobalFree:NEAR EXTERN GlobalAlloc:NEAR EXTERN GetFileSize:NEAR EXTERN CloseHandle:NEAR EXTERN CreateFileA:NEAR EXTERN ReadFile:NEAR EXTERN GetStdHandle:NEAR EXTERN WriteConsoleA:NEAR EXTERN ExitProcess:NEAR EXTERN GetCommandLineA:NEAR GlobalFree@4 = GlobalFree GlobalAlloc@8 = GlobalAlloc GetFileSize@8 = GetFileSize CloseHandle@4 = CloseHandle CreateFileA@28 = CreateFileA ReadFile@20 = ReadFile GetStdHandle@4 = GetStdHandle WriteConsoleA@20 = WriteConsoleA ExitProcess@4 = ExitProcess GetCommandLineA@0 = GetCoramandLineA ENDIF ;Директивы компоновщику для подключения библиотек. IFDEF MASM ;Для компоновщика LINK.EXE. includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib ELSE ;Для компоновщика TLINK32.EXE. includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' LENS DWORD ? ;Количество выведенных символов. HANDL DWORD ? ;Дескриптор консоли. HF DWORD ? ;Дескриптор файла. SIZEH DWORD ? ;Старшая часть длины файла. SIZEL DWORD ? ;Младшая часть длины файла. GH DWORD ? ;Указатель на блок памяти. NUMB DWORD ? BUF DB 100 DUP (0) _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ;Получить дескриптор вывода. PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL,EAX ;Получить количество параметров. CALL NUMPAR CMP EAX,2 JB _EXIT ;------------------------------------------------ ;Получить параметр номером EDI. MOV EDI,2 LEA EBX,BUF CALL GETPAR ;Теперь работаем с файлом. ;Открыть только для чтения. PUSH 0 PUSH 0 PUSH OPEN_EXISTING PUSH 0 PUSH 0 PUSH GENERIC_READ PUSH OFFSET BUF CALL CreateFileA@28 CMP EAX,-1 JE _EXIT ;Запомнить дескриптор файла. MOV HF,EAX ;Определить размер файла. PUSH OFFSET SIZEH PUSH EAX CALL GetFileSize@8 ;Запомнить размер, предполагаем, что размер не превосходит 4 Гбайт. MOV SIZEL,EAX ;Запросить память для считывания туда файла. PUSH EAX PUSH 0 CALL GlobalAlloc@8 CMP EAX,0 JE _CLOSE ;Запомнить адрес выделенного блока. MOV GH,EAX ;Читать файл в выделенную память. PUSH 0 PUSH OFFSET NUMB PUSH SIZEL PUSH GH PUSH HF CALL ReadFile@20 CMP EAX,0 JE _FREE ;Вывести прочитанное. PUSH 0 PUSH OFFSET LENS PUSH SIZEL PUSH GH PUSH HANDL CALL WriteConsoleA@20 _FREE: ;Освободить память. PUSH GH CALL GlobalFree@4 ;Закрыть файлы. _CLOSE: PUSH HF CALL CloseHandle@4 _EXIT: ;Конец работы программы. PUSH 0 CALL ExitProcess@4 ;**************************** ;Область процедур. ;**************************** ;Определить количество параметров (->ЕАХ) NUMPAR PROC CALL GetCommandLineA@0 MOV ESI,EAX ;Указатель на строку. XOR ECX,ECX ;Счетчик. MOV EDX,1 ;Признак. @@L1: CMP BYTE PTR [ESI],0 JE @@L4 CMP BYTE PTR [ESI],32 JE @@L3 ADD ECX,EDX ;Номер параметра. MOV EDX,0 JMP @@L2 @@L3: OR EDX, 1 @@L2: INC ESI JMP @@L1 @@L4: MOV EAX,ECX RET NUMPAR ENDP ;Получить параметр. ;EBX - указывает на буфер, куда будет помещен параметр. ;В буфер помещается строка с нулем на конце. ;EDI - номер параметра. GETPAR PROC CALL GetCommandLineA@0 MOV ESI,EAX ;Указатель на строку. XOR ECX,ECX ;Счетчик. MOV EDX,1 ;Признак. @@L1: CMP BYTE PTR [ESI],0 JE @@L4 CMP BYTE PTR [ESI],32 JE @@L3 ADD ECX,EDX ;Номер параметра. MOV EDX,0 JMP @@L2 @@L3: OR EDX,1 @@L2: CMP ECX,EDI JNE @@L5 MOV AL,BYTE PTR [ESI] CMP AL,32 JE @@L5 MOV BYTE PTR [EBX],AL INC EBX @@L5: INC ESI JMP @@L1 @@L4: MOV BYTE PTR [EBX],0 RET GETPAR ENDP _TEXT ENDS END START
Трансляция программы:
ML /с /coff /DMASM pr81_1.asm LINK /SUBSYSTEM:CONSOLE pr81_1.obj
TASM32 /ml pr81_1.asm TLINK32 -ap pr81_1.obj
Операционная система Windows предоставляет также группу функций, осуществляющих управление виртуальной памятью. Основной функцией этой группы является функция VirtualAlloc. Вот параметры этой функции.
Суть данной функции заключается в том, что вы можете зарезервировать блок памяти, который не спроецирован на физическую память, а затем сделать так, чтобы этот блок (или часть его) был спроецирован на физическую память. После чего этот блок памяти можно уже использовать.
Другая функция, VirtualFree, может освобождать блоки, задействованные функцией VirtualAlloc. Первым параметром этой функции является адрес блока. Вторым параметром функции является размер освобождаемого блока. Третий параметр функции может принимать значение MEM_DECOMMIT либо значение MEM_RELEASE. В первом случае блок (или его часть) перестает быть отображаемым. Во втором случае весь блок перестает быть зарезервированным. При этом значении второй параметр обязательно должен быть равен нулю.
На следующем шаге мы рассмотрим фильтры.