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

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


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


В прошлом выпуске мы познакомились с процедурами добавления логических объектов памяти, теперь же рассмотрим их удаление и связанные с этим проблемы. Само удаление труда не представляет, нужно лишь убрать объект из цепочки. Но тогда появляется небольшая проблема, мы получаем пустую ячейку памяти, которую менеджер памяти считает занятой. В связи с этим после удаления некоторого количества блоков памяти, надо проводить дефрагментацию страниц памяти. Способов произвести дефрагментацию много, но в нашем случае наиболее разумным будет сделать следующее. При удалении блока памяти сохраняется адрес удаленного куска памяти, это позволяет избежать поиска удалённых блоков, что достаточно выгодно при наличии большого количества блоков. После того как x блоков было удалено (на самом деле просто помечены на удаление), вызывается процедура дефрагментации памяти, которая копирует последние x блоков памяти на места удалённых. Естественно для хранения адресов удалённых блоков памяти нам понадобится выделять память самим, но думаю, эта жертва будет оправдана. Теперь рассмотрим процедуру удаления блока:
_asmGLmem_DeleteObject@4 proc
 mov eax,[esp+04h]         ;получаем адрес удаляемого блока памяти в eax
 push esi                  ;сохраняем esi
 assume eax:PTR OBJECT_OBJECT
 assume esi:PTR OBJECT_OBJECT
 cmp [eax]._type, GL_OBJECT
 jne last_object           ;Если этот блок памяти содержит объект
loop_del_object:           ;Этот цикл поблочно удаляет содержимое объекта
 push eax
 run _asmGLmem_GetChainEnd@4, eax
 cmp eax,[esp]
 jz last_object_2
 run _asmGLmem_DeleteObject@4, eax
 pop eax
 jmp short loop_del_object
last_object_2:
 pop eax
last_object:                ;Само удаление блока
 mov [eax]._flags, FLAG_DELETED
 mov esi,[eax]._next
 mov eax,[eax]._last
 cmp [eax]._type, GL_OBJECT
 jne not_object
 mov [eax]._child,esi
 jmp short is_object
not_object:
 mov [eax]._next,esi
is_object:
 test esi,esi
 jz no_next_object
 mov [esi]._last,eax
no_next_object:
 assume eax:nothing
 assume esi:nothing
 run DefragmentIt, dword ptr [esp+08h];Вызов функции дефрагментации
 pop esi
 ret 4
_asmGLmem_DeleteObject@4 endp
Как Вы видите, функция удаления блока памяти в обязательном порядке вызывает функцию DefragmentIt. Именно она сохраняет адреса удалённых блоков, и дефрагментирует память при необходимости. Выглядит она следующим образом:
DefragmentIt proc
 pusha              ;сохраняем регистры общего назначения
 null ebx           ;ebx = 0
 mov esi,Deleted    ;стек удалённых блоков в esi
 mov eax,[esi]      ;eax = количество адресов в стеке удалённых блоков
 lea eax,[eax*4+04h]
 add eax,esi        ;eax = адрес следующей ячейки в стеке удалённых блоков
 mov ecx,[esp+24h]  ;ecx = адрес удаляемого блока
 mov [eax],ecx      ;записываем адрес удаляемого объекта в стек
 inc dword ptr [esi];увеличиваем количество адресов в стеке
 mov eax,MaxDel
 cmp eax,[esi]      ;сравниваем количество адресов в стеке с его пределом
 jnz EndDefragmentIt;Если предел достигнут не был, то просто закончить процедуру
 lea esi,[esi+eax*4];esi = конец стека
LoopDefragmentIt:
 mov edi,CurPage
 mov eax,FreeItems
 shl eax,7
 lea ecx,[edi+eax]  ;ecx = адрес последнего блока памяти
 run CheckIfDeleted, ecx;Проверяем, не был ли он удалён (см. процедуру ниже)
 test eax,eax
 jnz main_ready     ;Если блок был удалён, то ничего копировать не надо
 run CopyDwords, dword ptr [esi], ecx, 32
;Следующие строки изменяют все адреса указывающие на скопированный блок
 assume ecx:PTR OBJECT_OBJECT
 assume eax:PTR OBJECT_OBJECT
 mov eax,[ecx]._last
 test eax,eax
 jz parent_ready
 cmp [eax]._next,ecx
 jnz parent_object
 m2m [eax]._next,dword ptr [esi]
 jmp short parent_ready
parent_object:
 m2m [eax]._child,dword ptr [esi]
parent_ready:
 mov eax,[ecx]._next
 test eax,eax
 jz main_ready
 m2m [eax]._last,dword ptr [esi]
 cmp [ecx]._type, GL_OBJECT
 jne main_ready
 mov eax,[ecx]._child
 m2m [eax]._last,dword ptr [esi]
 assume eax:nothing
 assume ecx:nothing
;После того, как последняя запись о бывшем месте блока была удалена, нужно удалить сам блок
main_ready:
 inc FreeItems      ;уменшаем количество занятых блоков
 cmp FreeItems, 1023
 je free_page       ;Если страница памяти осталась пустой то нужно её удалить
page_ok:            ;когда страница удалена управление поступает сюда
 mov eax,Deleted
 dec dword ptr [eax];уменьшим длину стека удалённых блоков
 jz EndDefragmentIt ;если все адреса были обработаны, то закончить процедуру
 mov [esi],ebx      ;Это гарантирует, что один и тот-же адрес не будет использован дважды
 sub esi,04h        ;изменяем вершину стека (получаем адрес следующего удалённого блока)
 cmp [esi],ebx      ;Если он уже был обработан
 jz page_ok
 jmp LoopDefragmentIt;Продолжаем цикл
;Следующие строки удаляют пустую страницу памяти
free_page:
 mov ecx,PageCount
 dec PageCount
 push PageCount
 mov eax, MainBlock
loop_free_page:
 dec ecx
 jz end_loop_free_page
 add eax, 1023*128
 mov eax, [eax+18h]
 jmp short loop_free_page
end_loop_free_page:
 run HeapFree, hHeap, ebx, eax
 pop ecx
 mov eax, MainBlock
loop_free_page2:
 add eax, 1023*128
 dec ecx
 jz end_loop_free_page2
 mov eax, [eax+18h]
 jmp short loop_free_page2
end_loop_free_page2:
 mov [eax+18h],ebx
 sub eax, 1023*128
 mov CurPage, eax
 mov FreeItems, ebx
 jmp page_ok
;Завершение процедуры
EndDefragmentIt:
 popa
 ret 4
DefragmentIt endp

;Следующая функция лишь проверяет бит удалённости блока памяти и, если он удалён, удаляет его
;из списка
CheckIfDeleted proc
 null eax
 pusha
 mov edx,[esp+24h]
 assume edx:PTR OBJECT_OBJECT
 cmp [edx]._flags, FLAG_DELETED
 jz found_CID
 popa
 ret 4
found_CID:
 mov esi,Deleted
 mov ecx,esi
loop_CID:
 add ecx,4
 cmp [ecx],edx
 jnz loop_CID
 mov [ecx],ebx
 mov eax,Deleted
 inc dword ptr [eax]
 popa
 add esi,04h
 inc eax
 ret 4
CheckIfDeleted endp
Как Вы видите, мы работаем со стеком удалённых блоков. Как только стек достигает определённой отметки, начинается копирование последних блоков памяти (если они не были удалены) на места помеченных на удаление. Система достаточно проста, но существует огромное количество нестандартных ситуаций, из-за которых код процедуры становится достаточно непонятным. Но, думаю, нет большёго смысла теребить пустой код. В следующем выпуске мы рассмотрим теоретическую работу библиотеки asmGLmem и рассмотрим её работу в "близких к боевым" условиях...


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

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