AMIGA REVIEW obsah časopisu online!
  Domov     Software     Hry     Obaly     Download  

Assembler

Jan 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

AMIGA REVIEW

57 ( 11-12 / 2000 )
56 ( 9-10 / 2000 )
55 ( 7-8 / 2000 )
54 ( 5-6 / 2000 )
53 ( 3-4 / 2000 )
52 ( 1-2 / 2000 )
 
51 ( 12 / 1999 )
50 ( 11 / 1999 )
49 ( 10 / 1999 )
48 ( 9 / 1999 )
46-47 ( 7-8 / 1999 )
45 ( 6 / 1999 )
44 ( 5 / 1999 )
43 ( 4 / 1999 )
42 ( 3 / 1999 )
41 ( 2 / 1999 )
40 ( 1 / 1999 )
 
39 ( 12 / 1998 )
38 ( 11 / 1998 )
37 ( 10 / 1998 )
36 ( 9 / 1998 )
35 ( x / 1998 )
34 ( x / 1998 )
33 ( 1-2 / 1998 )
 
32 ( 11-12 / 1997 )
31 ( 9-10 / 1997 )
30 ( 7-8 / 1997 )
29 ( 6 / 1997 )
28 ( 5 / 1997 )
27 ( 4 / 1997 )
26 ( 3 / 1997 )
25 ( 2 / 1997 )
24 ( 1 / 1997 )
 
23 ( 12 / 1996 )
22 ( 11 / 1996 )
21 ( 10 / 1996 )
20 ( 9 / 1996 )
18-19 ( 7-8 / 1996 )
17 ( 6 / 1996 )
16 ( 5 / 1996 )
15 ( 4 / 1996 )
14 ( 3 / 1996 )
13 ( 2 / 1996 )
12 ( 1 / 1996 )
 
11 ( 12 / 1995 )
10 ( 11 / 1995 )
9 ( 10 / 1995 )
8 ( 9 / 1995 )
7 ( 7 / 1995 )
6 ( 5 / 1995 )

ATLANTIDA NEWS

5 ( 3 / 1995 )
4 ( 1 / 1995 )
 
3 ( 11 / 1994 )
2 ( 9 / 1994 )
1 ( 7 / 1994 )
0 ( 5 / 1994 )