Доброго времени суток, уважаемые подписчики. К сожалению, отладка процедуры дефрагментации заняла гораздо больше времени, чем я рассчитывал... В этом выпуске мы рассмотрим возможности дефрагментации памяти блочным менеджером памяти.
В прошлом выпуске мы познакомились с процедурами добавления логических объектов памяти, теперь же рассмотрим их удаление и связанные с этим проблемы. Само удаление труда не представляет, нужно лишь убрать объект из цепочки. Но тогда появляется небольшая проблема, мы получаем пустую ячейку памяти, которую менеджер памяти считает занятой. В связи с этим после удаления некоторого количества блоков памяти, надо проводить дефрагментацию страниц памяти. Способов произвести дефрагментацию много, но в нашем случае наиболее разумным будет сделать следующее. При удалении блока памяти сохраняется адрес удаленного куска памяти, это позволяет избежать поиска удалённых блоков, что достаточно выгодно при наличии большого количества блоков. После того как 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 и рассмотрим её работу в "близких к боевым" условиях...