Шаг 81.
Управление памятью

    На этом шаге мы рассмотрим функции, используемые для управления памятью.

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

    Начнем с функции 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	
Текст этого файла можно взять здесь.

    Трансляция программы:

    Операционная система Windows предоставляет также группу функций, осуществляющих управление виртуальной памятью. Основной функцией этой группы является функция VirtualAlloc. Вот параметры этой функции.

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

    Другая функция, VirtualFree, может освобождать блоки, задействованные функцией VirtualAlloc. Первым параметром этой функции является адрес блока. Вторым параметром функции является размер освобождаемого блока. Третий параметр функции может принимать значение MEM_DECOMMIT либо значение MEM_RELEASE. В первом случае блок (или его часть) перестает быть отображаемым. Во втором случае весь блок перестает быть зарезервированным. При этом значении второй параметр обязательно должен быть равен нулю.

    На следующем шаге мы рассмотрим фильтры.




Предыдущий шаг Содержание Следующий шаг