Ассемблер под Windows №31

Менеджер блочной памяти


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


Как уже упоминалось, наш менеджер памяти будет оперировать блоками памяти. Это имеет один большой минус - даже самый маленький блок памяти будет занимать 128 байт. Но имеет следующие плюсы:
- Меньшая фрагментированность памяти.
- Упрощённая работа с памятью (не нужды в куче переменных для работы менеджера)
Далее, менеджер памяти будет находиться в отдельной библиотеке, asmGLmem.dll. Вот её код:
include kernel32.inc
include def32.inc
include macros.inc
include objects.inc
 .386
 .model flat
 option casemap:none

 public _asmGLmem_AddObject@8
 public _asmGLmem_GetChainEnd@4

_BSS SEGMENT public USE32 'bss'
hHeap     dd ?
MainBlock dd ?
CurPage   dd ?
FreeItems dd ?
PageCount dd ?
_BSS ENDS

_TEXT SEGMENT public USE32 'code'
_start@12:
 push ebx         ;сохраняем ebx
 mov eax,[esp+0Ch];получаем в eax второй параметр функции, причину вызова
 push esi         ;сохраняем esi
 null ebx         ;ebx = 0
 dec eax
 js detach_process;если (eax+1) <= 0 то библиотека выгружается
 jnz end_start    ;если (eax+1) != 1, то библиотека и не загружается и не выгружается
attach_process:   ;в оставшемся слечае (eax+1)=1, библиотека загружается
 mov esi,128*1024 ;esi = размер логической страницы менеджера памяти (128 Кб)
 run HeapCreate, ebx, esi, ebx
 test eax,eax     ;Если не получилось создать "кучу", то выход с ошибкой
 jz error_start
 mov hHeap,eax    ;Сохраняем handle "кучи"
 run HeapAlloc, eax, HEAP_ZERO_MEMORY, esi
 test eax,eax     ;Создаём первую страницу, если не получилось - выходим с ошибкой
 jz error_start
 mov MainBlock,eax;Заполняем переменные менеджера памяти
 mov CurPage,eax
 pusha
 shr esi,7
 dec esi
 mov FreeItems,esi
 inc PageCount
 popa
 lea eax,[eax+esi-128];Получаем в eax адрес предпоследнего блока в странице памяти
 mov dword ptr [eax],GL_PAGEADDRESS;Резервируем его для нужд менеджера
 jmp short end_start
detach_process:   ;Если библиотека выгружается
 run HeapDestroy, hHeap
end_start:        ;Нормальное завершение функции
 pop esi          ;Восстанавливаем esi
 null eax         ;eax = 0
 pop ebx          ;Восстанавливаем ebx
 inc eax          ;eax = 0+1 = 1
 ret 12           ;Возвращаем eax = 1
error_start:      ;Завершение функции с ошибкой
 pop esi          ;Восстанавливаем esi
 null eax         ;eax = 0
 pop ebx          ;Восстанавливаем ebx
 ret 12           ;Возвращаем eax = 0

_asmGLmem_AddObject@8 proc
 push ebp         ;создаём стековый кадр
 mov ebp,esp
 push esi         ;сохраняем esi
 mov esi,CurPage
 mov eax,FreeItems
 shl eax,10
 add eax,-128
 add esi,eax      ;Генерируем адрес свободной ячейки памяти
 run CopyDwords, esi, dword ptr [ebp+08h], 32
 run _AsmGLmem_GetChainEnd@4, dword ptr [ebp+0Ch]
 assume esi:PTR OBJECT_POINT
 assume eax:PTR OBJECT_OBJECT
 mov [esi]._last, eax
 test eax,eax     ;Если нет объекта-родителя то завершаем функцию
 jz not_object__
 cmp [eax]._type, GL_OBJECT
 je object__      ;Если есть объект-родитель то заносим адрес дочерного объекта в поле _child
 mov [eax]._next,esi;Если объект часть цепочки, то в поле _next
 jmp short not_object__
object__:
 mov [eax]._child,esi
not_object__:
 assume esi:nothing
 assume eax:nothing
 dec FreeItems
 jnz added_ok     ;Если ещё осталить пустые ячейки, то закончить
 push esi
 mov esi,128*1024
 run HeapAlloc, hHeap, HEAP_ZERO_MEMORY, esi
 test eax,eax
 jnz allocated_ok
 ...               ;Если не хватает памяти, то надо будет что-то придумать
allocated_ok:
 mov ecx,CurPage
 lea ecx,[ecx+esi-128]
 mov [ecx+18h],eax
 mov CurPage,eax
 shr esi,7
 dec esi
 mov FreeItems,esi
 inc PageCount
 pop esi
added_ok:
 mov eax,esi      ;eax = esi = адрес добавленого блока
 pop esi          ;Восстанавливаем esi
 leave            ;Уничтожаем стековый кадр
 ret 8
_asmGLmem_AddObject@8 endp

_asmGLmem_GetChainEnd@4 proc
 mov eax,[esp+04h]
 assume eax:PTR OBJECT_OBJECT
 test eax,eax
 jz found__
 cmp [eax]._type, GL_OBJECT
 je object__
next__:
 cmp [eax]._next, 0
 jz found__
 mov eax,[eax]._next
 jmp short next__
object__:
 cmp [eax]._child,0
 jz found__
 mov eax,[eax]._child
 jmp short next__
 assume eax:nothing
found__:
 ret 4
_asmGLmem_GetChainEnd@4 endp

;Такие процедуры, как CopyDwords
...

 end _start@12
_TEXT ENDS
Как вы могли заметить, пока объявлены лишь 3 процедуры, одна из которых запускается при загрузке/выгрузке библиотеки. При загрузке библиотеки процедура создаёт "кучу" памяти и резервирует первую страницу (здесь я имею ввиду логический кусок памяти, который наш менеджер выделяет за раз = 128 килобайт, а не логическую страницу страничного режима работы процессора = 4 килобайта). При выгрузке она просто уничтожает "кучу". Функция GetChainEnd возвращает адрес последнего объекта в цепочке. Это нужно, например, когда есть объект, к которому добавляется один полигон. Так как полигоны объекта образуют цепочку, адрес которой находится в поле "child" объекта, то новый полигон следует добавлять не к самому объекту, а в конец цепочки. Функция AddObject осуществляет следующие действия: Копирует логический блок памяти в память менеджера, согласует адреса объекта родителя, цепочки полигонов и самого блока памяти, и резервирует новою страницу памяти, если текущая заполнилась.

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


На сегодня это всё. Если есть какие вопросы, пишите. Пишите на Dark_Lord@RusFAQ.ru или Dark_Lord@land.ru. Предыдущие выпуски можно найти на сайте рассылки

Сайт управляется системой uCoz