AssemblerJan Hlavatý
Tak vás opět vítám v našem seriálku o systému a assembleru. V této části se
podíváme na alokaci paměti. Paměť je asi na nejnižší úrovni ze všech zdrojů
potřebných pro běh programu, začneme tedy s ní.
Program při svém běhu obvykle potřebuje místo na ukládání různých svých
proměnných. Obvykle se místo pro tyto proměnné rezervuje přímo v programu v jeho
datové sekci. Tento způsob má ale některé nedostatky: V případě že předem nevíme
jak velké místo budeme potřebovat, je logicky nemožné ho začlenit do programu
(jako globální proměnnou), protože na to potřebujeme znát jeho přesnou velikost.
Navíc program s globálními proměnnými začleněnými v kódu programu není možné
spustit v multitaskingu ve více procesech najednou se stejným kódem, protože
všechny tyto procesy by sdílely stejné proměnné a vznikly by konflikty. A pokud
byste chtěli předat nějakou datovou strukturu systému tak aby mu zůstala i po
ukončení vašeho programu, nesmí tato být jeho součástí, protože po ukončení
programu všechno co bylo jeho součástí přestává existovat. Všechny tyto problémy
se lehce vyřeší dynamickým přidělováním volné paměti při běhu programu. Veškerá
paměť v systému je přidělována pomocí funkcí v "exec.library". Když program
potřebuje kus paměti požádá systém o jeho přidělení (alokaci). K tomu potřebuje
systém znát požadovanou velikost bloku paměti a typ paměti (CHIP, FAST nebo
jakoukoli). Systém pak prohledá volnou paměť a pokud najde vhodný blok paměti,
odstraní ho z volné paměti a jeho adresu vrátí programu. Alokace paměti však
vždycky může selhat z důvodu nedostatku volné paměti! V takovém případě vrátí
systém programu místo adresy bloku paměti nulu (NULL). Na to je třeba myslet při
psaní programu a nikdy nepředpokládat že se to nestane. V opačném případě hrozí
zhroucení programu, za což vám uživatel nepoděkuje - ba co víc, probudí to v něm
agresivní podněty vzhledem k autorově osobě (alespoň podle mých reakcí soudě).
Samozřejmě poté co program přestane alokovaný blok paměti potřebovat, musí ho
vrátit zpět systému - dealokovat. Poté co byl blok vrácen systému, za žádnou
cenu už s ním program nesmí pracovat - mezitím mohl být znovu přidělen jinému
programu a už tam může být cokoli jiného. Vrhněme se tedy k popisu funkcí pro
alokaci paměti: Základní funkce pro alokaci paměti se jmenuje AllocMem(). Jako
parametry vyžaduje v registru D0 délku požadovaného bloku paměti a v registru D1
typ paměti plus některé příznaky modifikující činnost AllocMem(). Typ paměti je
určen konstantami MEMF_CHIP, MEMF_FAST, MEMF_PUBLIC. MEMF_CHIP znamená že blok
musí být CHIP-ram, MEMF_FAST že to musí být FAST-ram a MEMF_PUBLIC že to má být
jakákoli paměť nejlépe Fast-ram. Funkci je možno požádat aby alokovaný blok
paměti vyplnila nulami pomocí MEMF_CLEAR. Dále na OS 2.0+ je možné požadovat
alokaci "z druhé strany" pomocí MEMF_REVERSE. Představte si následující situaci:
Alokujete nějaký malý dočasný buffer. Ten používáte a potom alokujete paměť pro
trvalé uložení výsledků. Pak už dočasný buffer nepotřebujete a vrátíte ho do
volné paměti. Protože alokační funkce prohledává volné bloky popořadě, vznikne
na místě bývalého dočasného bufferu "díra" - malý osamocený blok volné paměti.
Tomuto jevu se říká "fragmentace paměti" a je to jev negativní, protože tyto
bloky jsou příliš malé pro většinu alokací, ale příliš velké aby to neubralo
paměť. Celkový součet volné paměti (jak se jeví třeba na liště Workbenche) sice
souhlasí, ale protože je paměť rozsekaná do malých kousků, jakákoli alokace
bloku většího než je největší volný kousek paměti selže. Když si představíte že
by výše popisovaná teoretická situace byla uvnitř nějakého cyklu, mohla by v
paměti vzniknout pěkná sekaná. Navíc fragmentovaná paměť zpomaluje jakoukoli
další alokaci paměti protože je třeba otestovat větší množství bloků volné
paměti než v ideálním případě. Aby se fragmentaci paměti předešlo, může
programátor který ví že po nějakém bloku alokované paměti v jeho programu by
mohly zůstávat takovéto "díry" použít MEMF_REVERSE (pro onen dočasný buffer).
Tím se tento blok alokuje od konce paměti místo od začátku a díra nevznikne.
Posledním atributem který tu popíšu je MEMF_NO_EXPUNGE. Tento atribut funguje od
OS 3.0+ a má souvislost se speciálními mechanismy pro případné uvolnění paměti
při jejím akutním nedostatku. Na 3.0 zůstávají jednou nahrané knihovny a jiné
systémové objekty v paměti i když už nejsou potřeba, dokud nejsou násilím
vyhozeny (např. dosovým příkazem AVAIL FLUSH). V případě nedostatku paměti jsou
tyto "zbylé" věci z paměti postupně vyhazovány, dokud trvá nedostatek paměti. To
se může stát právě při volání AllocMem(). MEMF_NO EXPUNGE řekne AllocMem, že v
případě že nebude dost volné paměti NEMÁ vyhazovat věci z paměti. To je dobré
třeba pro alokaci nějakých pomocných bufferů které nejsou nezbytně nutné ale
proč je nemít, když je dost paměti. Funkce AllocMem() vrací v registru D0 adresu
alokovaného bloku nebo nulu pokud není dost paměti. Všechny specifikované
atributy se spojí dohromady funkcí OR (nebo sečtou) - každá tato konstanta
odpovídá jednomu bitu. Nikdy nesmíte zkombinovat MEMF_CHIP s MEMF_FAST - vždycky
smíte uvést jen jeden typ paměti. Ve skutečnosti se bloky paměti alokují v
násobcích 8 byte, a jejich začátky leží vždy na adrese dělitelné 8. Toho se dá
využít např. při alokaci bitplánů pro AGA zobrazovací módy, které vyžadují aby
začátek bitplánu byl vždy na adrese dělitelné 8-mi.
Opakem AllocMem() je funkce FreeMem(). Jako parametry vyžaduje adresu bloku
(kterou vrátil AllocMem()) v registru A1 a jeho délku (stejnou jako u AllocMem)
v registru D0. Jakmile jednou paměť uvolníte, přestává pro vás existovat!
Speciální variantou alokace je funkce AllocAbs(). Ta umožní alokovat nějaký
konkrétní úsek paměti (tj. známe jeho adresu a délku a chceme ho odstranit z
volné paměti). Ta však najde použití jen zřídka.
Podobným párečkem funkcí jako AllocMem/FreeMem je dvojice AllocVec/FreeVec.
AllocVec() má stejné parametry jako AllocMem() a funguje stejně s jedním
rozdílem: Alokuje blok o kousek větší a do takto získaného místa si poznamená
délku bloku, aby při jeho uvolnění nemusela být znovu zadávána (a stejně někde
zapamatována). Takto alokovanou paměť pak lze zase uvolnit pomocí funkce
FreeVec(), která má jediný parametr - adresu bloku v A1 . Délka bloku se
vyzvedne automaticky. V případě AllocVec mám dojem, že paměť takto alokovaná má
zaručeno že leží na adrese dělitelné 4 a ne osmi.
V souvislosti s alokací paměti se zmíním ještě o funkci AvailMem(), která slouží
ke zjištění informací ohledně velikostí volné paměti. Jako parametr v registru
D1 vyžaduje atributy paměti (MEMF_CHIP, MEMF_FAST), plus pomocí MEMF_LARGEST je
možné požádat funkci aby vrátila ne celkový počet volných byte paměti daného
typu, ale velikost největšího volného bloku paměti daného typu (viz výše o
fragmentaci paměti).
To je pro dnešek (vzhledem k technickým problémům s mym harddiskem - brousí)
skoro všechno, následuje malý příkládek na používání alokace paměti. Doporučuji
sehnat si systémové include soubory pro assembler, protože budu používat jména
konstant tam uvedených bez vypisování jejich hodnoty. Jsou k sehnání na většině
amigáckých BBS a je to věc k programování nezbytná. Kromě definic konstant jsou
tam k nalezení i komentáře, ze kterých člověk ledacos zjistí...
;=====================
;příklad na alokaci paměti
;=====================
;příklad - jak udělat globální proměnné aby ;výsledný kód mohl být "pure"
_LVOFreeMem = -210 ;a1 :ptr,d0: size
_LVOAllocMem = -198 ;d0 :size,d1: attr
;definice struktury glob.dat
RSRESET
returncode rs.l 1
global_long rs.l 1
global_word rs.l 1
velikost_glob rs.l 0
SECTION TEXT,CODE
start move.l 4.w,a6 ;báze exec do a6
move.l #velikost_glob,d0
move.l #MEMF_PUBLIC!MEMF CLEAR,d1
jsr _LVOAllocMem(a6)
tst.l d0
beq neniram
move.l d0,a5 ;a5 bude stále ukazovat na náš blok
move.l global_long(a5),d2
move.l #1,returncode(a5)
konec move.l returncode(a5),d2
move.l a5,a1
move.l #velikost_glob,d0
move.l 4.w,a6
jsr _LVOFreeMem(a6)
move.l d2,d0
rts
neniram moveq #20,d0
rts Vytlačiť článok
Pozn.: články boli naskenované ako text a preto obsahujú aj zopár chýb. Taktiež neručíme za zdrojové kódy (Asm, C, Arexx, AmigaGuide, Html) a odkazy na web. Dúfame, že napriek tomu vám táto databáza dobre poslúži.
Žiadna časť nesmie byť reprodukovaná alebo inak šírená bez písomného povolenia vydavatela © ATLANTIDA Publishing
none
|