Memória


A számítógépekben használt memóriákat az elérés módja szerint két nagy csoportba sorolhatjuk: cím szerinti illetve tartalom szerinti elérést biztosító memóriákra. Az első csoport az, amelyet általában központi memóriának vagy csak egyszerűen memóriának szoktunk nevezni, míg a második csoport az asszociatív memóriák csoportja.

Memóriatípusok:

Cím szerinti elérést biztosító memóriák:

A cím szerinti elérést biztosító memória felépítését mutatja az alábbi ábra:

Az R/W vonalon érkező jel vezérli azt, hogy olvasás (R) vagy írás (W) történjen. Az alábbi ábra a címezhető memóriák fáját mutatja, tisztán hardveres csoportosításban:

RWM-nek (read/write memory - írható/olvasható memória) nevezzük azt a memóriát, amelynek tartalma egy adott cím szerint kiolvasható illetve megváltoztatható.

RAM-nak (random access memory - véletlen elérésű memória) nevezzük azt a memóriát, amelynél egy tetszőleges elem elérése, megcímzése, ugyanannyi ideig tart. A szakterminológia felruházza a RAM-okat azzal a tulajdonsággal, hogy ezek a memóriák - a véletlen elérés mellett - írható/olvasható memóriák. A mai számítógépek központi memóriája szinte kivétel nélkül RAM chip-ekből áll.

ROM-nak (read only memory - csak olvasható memória) azt a memóriát nevezzük, amelynek tartalma nem módosítható (írható), csak olvasható. A gyakorlatban ezek a memóriák is véletlen elérésűek, vigyázzunk a terminológia gyengeségére! A ROM memóriákat operációs-rendszer magok (BIOS) és egyéb célprogramok tárolására szokás használni.

Az SRAM (statikus RAM) olyan memóriachip, amely állandó feszültség hatására működik, a DRAM (dinamikus RAM) az állandó feszültség mellett rendszeres frissítést, kiolvasást igényel.

A ferritmemória nagysága és lassúsága miatt lassan kihal, egy olyan fontos tulajdonsága van, amellyel a chip-ek nem tudnak konkurálni, nevezetesen az, hogy nem szükséges feszültség ahhoz hogy megőrizze a tartalmát.

A maszkolt ROM olyan chip, amelybe a gyártó belehelyezi a tartalmát, az a későbbiek során nem módosítható.

A PROM (programozható ROM) olyan chip, amelyet tartalom nélkül gyártanak és egyszer írhatunk bele. Ezt az írást nevezik beégetésnek, az egyes memóriacellák tartalmát úgy változtatják meg, hogy olyan feszültséget adnak a cellára, hogy az kiég.

Az EPROM (erasable PROM -törölhető PROM) olyan memóriachip, amelynek tartalma törölhető, majd újraírható. A törlés kétféle lehet: UV fénnyel vagy elektromos áram segítségével történhet. Az UV-vel törölhető EPROM-okat könnyű felismerni a chip tokjának tetején levő kis ablakról.

Asszociatív memóriák:

Az asszociatív memóriák olyan cellákból állnak, amelyek egy kulcsból, egy - a kulcshoz rendelt - értékből és egy összehasonlító áramkörből állnak. A memória alapvetően a keresések meggyorsítására való, egységnyi idő alatt képes megkeresni egy adott kulcshoz tartózó elemet, bármekkora is legyen az asszociatív memória.

A működése során a bemenetére adott keresendő kulcsot (KK) az összes összahasonlítóáramkör összehasonlítja a tárolt kulccsal, és ha a két kulcs megegyezik, akkor a tárolt értéket "kiengedi". Ha nem jelenik meg érték a kimeneten, az azt jelenti, hogy egyik összehasonlító áramkör sem talált egyezőséget, így a keresett kulcs nincs az asszociatív memóriában.

A memória-hierarchia:

A következő ábra a számítógépben elhelyezkedő, adat illetve programtárolásra szolgáló eszközöket mutatja:

A baloldali számoszlop az adott eszköz kapacitását, a jobboldali pedig az elérési idejét mutatja. A két cache memória abban különbözik a többitől, hogy ezek nem adnak ténylegesen többletmemóriát, szerepük a memóriarendszer gyorsítása.

Cache memória:

A cache memória a központi memória és a processzor között helyezkedik el és célja a memória elérésének meggyorsítása. Nem más, mint egy asszociatív memória, amelyben a kulcsok szerepét a memóriacím játssza, az értékek szerepét pedig az adott címen elhelyezkedő adat. Így amikor a processzor megcímzi a memóriát, hogy onnan olvasson, akkor először a cache kapja meg a címet. Ha itt megtalálható az adott cím (mint kulcs), akkor a hozzá tartozó érték továbbításra kerül a processzorba. Ha nem, akkor cache-miss (azaz cache nem találja) állapot lép fel és az adott adatért el kell menni a központi memóriába és betölteni onnan (a cache-be és a processzorba). Írás esetén a cím a hozzá tartozó adattal egyből a cache-be kerül, majd innen továbbítódik a memóriába. Problémát okoz többprocesszoros környezetbe az, ha minden processzor saját cache-sel rendelkezik, ugyanis ekkor a memóriavezérlőnek figyelnie kell arra, hogy ha egy memóriatartalom megváltozik, akkor annak változása az összes cache-ben tükröződjön. Annak oka, hogy - a központi memóriához képest - viszonylag kisméret cache memória is tekintélyes gyorsulást okoz, abban rejlik, hogy a programok lokálisak. A lokalitás részletes magyarázatát lásd a "Memóriakezelés" fejezetben.

Diszk Cache:

A diszk cache nem más, mint egy olyan memóriaterület, ahol a lemezről egyszer már beolvasott adatok tárolódnak. Azaz egy olyan "asszociatív memória", ahol a kulcs nem más mint egy adott adatblokk címe a lemezen, az érték pedig az adott adatblokk tartalma. A diszk cache - szemben a memóriacache-el - szinte minden esetben szoftver, amely figyeli a lemezműveleteket és megpróbálja csökkenteni a fizikai műveletek számát. Két alapvető megoldási módszer létezik: az első esetben a diszk cache számára szükséges memória a központi memória egy területe, míg a másik esetben a lemezt vezérlő egységen helyezik el ezt a memóriát. Mindkét módszernek vannak előnyei és hátrányai.

A központi memóriában levő diszk cache esetén a cache programot a számítógép processzora futtatja. Ez a processzor leterhelésének fejében azt az előnyt adja, hogy a cache program tudhat a lemez logikai felépítéséről, a file-ok szervezéséről így ennek függvényében optimalizálhat. Például ha egy file elejét olvassuk, akkor célszerű nem csak az első blokkot beolvasni, hanem a továbbiakat is, ezt a módszert előolvasásnak (read ahead) nevezzük. További előny még, hogy ha az olvasni kívánt adat már a cache-ben van, akkor nem szükséges a diszk-vezérlőt dolgoztatni. Írás esetén a cache program tudhatja, hogy melyek azok a lemezblokkok, amelyek a file-rendszer létfontosságú részei, így azok esetében nem alkalmazhatja a késleltetett kiírást. A késleltetett írás (delayed write) azt jelenti, hogy a kiírni kívánt lemezblokk bekerül a cache-be és onnan csak egy bizonyos idő elteltével kerül a lemezre. Ez a technika - bár igen jelentős mértékben megnöveli a rendszer teljesítményét - életveszélyes, ha valamilyen ok (pl. áramkimaradás) miatt a cache-ben levő blokkok sohasem kerülnek fel a lemezre.
A lemezvezérlőn levő cache bár nem foglalja le a központi processzor teljesítményét, pusztán fizikai jellemzők alapján tud okoskodni. Például az előolvasás számára azt jelenti, hogy ha egy sáv egy adott szektorát kívánjuk beolvasni, akkor beolvassa az egész sáv tartalmát (ha a file-ok töredezettek, akkor ez a technika semmit sem ér). Nagy hibája még a rendszernek, hogy a lemezvezérlő és a memória között minden esetben át kell vinni az adott adatblokkot, így ennek a megvalósításnak csak igen gyors kapcsolat esetén van létjogosultsága. (Van még egy eset, nevezetesen az, amikor a központi processzoron olyan operációs rendszer fut, amelynek nincs vagy igen gyenge cache programja van... )

Memóriakezelés:

Ebben a részben megtudhatod, hogy a memória-kezelés milyen feladatokat ró az operációs rendszerre. Ezt a vizsgálatot az operációs rendszerek típusai szerint fogod látni. Ehhez persze szükség van arra, hogy definiáljuk az operációs rendszer legfontosabb feladatait (bár elsőban tanították).

Az operációs rendszer:

Az operációs rendszer a számítógép legfontosabb programja, e nélkül egy gép pusztán ócskavas volna. Egy mondattal úgy határozhatjuk meg az operációs rendszer feladatát, hogy az nem más, mint a rendelkezésre álló erőforrások elosztása a rendszert használó felhasználók és futtatott programjaik között. Az erőforrások lehetnek hardver erőforrások (processzoridő, memóriaterület, lemezterület, I/O egységek használati joga, hálózati elérés) illetve szoftver erőforrások (file-rendszer, egyes programok használati joga).

A legegyszerűbb operációs rendszer típus a batch-monitor. Itt egy időben egy program futtatására van lehetőség, illetve a programok kötegekbe (batch) foghatók és a kötegbe foglalt programok egymás utáni végrehajtását kérhetjük az operációs rendszertől. Két tipikus példa erre az IBM 360-as sorozatnál bevezetett DOS illetve az IBM PC-knél monopolhelyzetben levő MS-DOS. (A két rendszer közt csak annyi a lényegi különbség, hogy a fiatalabbik rendszer interaktív, azaz a felhasználó nem lyukkártyákkal kommunikál, hanem a képernyő illetve a billentyűzet segítségével.)
A batch-monitorok jellemzője, hogy mivel egy időben csak egy programmal avagy programköteggel dolgoznak, csak egy felhasználót tudnak kiszolgálni, azaz egyfehasználós, egyprogramos rendszerek (single user, single program/task) rendszere.

A következő nagy csoport az egy felhasználós, de több program egyidejű futtatására képes (single user, multitasking) rendszerek. Több program egyidejű futása alatt azt kell érteni, hogy az operációs rendszer megosztja a processzoridőt a futó programok között, így úgy látszik a felhasználó számára, hogy a programjai egyszerre futnak. (Ha több processzor van a gépben, akkor persze valóban futhatnak ezek a programok egy időben!) Ilyen operációs rendszer a MS Windows, az OS/2 illetve a MacOS.
Ha több program, task fut kvázi-párhuzamosan, akkor felmerül az a kérdés, hogy a processzor ideje hogyan legyen felosztva közöttük. Két, lényegesen különböző megoldás lehetséges.

Preemtive (megszakításos) rendszer esetén az operációs rendszer utalja ki a futásra képes task számára a processzoridőt, és amikor az lejár, akkor megszakítja a task végrehajtását és egy másikat választ.

Non-preemtive (nem megszakításos) rendszer esetén a futó task-nak kell úgy döntenie, hogy visszaadja a processzort az operációs rendszernek, hogy jelöljön ki egy másik task-ot futásra. A második módszer természetesen csak egyfelhasználós környezetben használható, többfelhasználós környezetben nem megengedhető az, hogy egy "mohó" program ne engedje a többit futni. Ahhoz, hogy egy non-preemtive rendszer párhuzamosnak tűnjön a felhasználó számára az szükséges, hogy a programok kellően "előzékenyek" legyenek és elég sűrűn kérjék meg az operációs rendszert a task-cserére (pl. MS-Windows).
A preemptive rendszereknél a legfontosabb kérdés az egyes task-ok számára kiutalt idő mennyisége. Több módszer is létezik ennek szabályozására:

Merev proiritásos rendszer esetén egy program akkor szakíthat meg egy másikat, ha nagyobb a prioritása, azaz erősebb. Ilyenkor a nagyon erős programok jóval nagyobb időintervallumokat képesek kihasítani, mint a gyengék. A legerősebb program természetesen az operációs rendszer, hiszen neki minden más programot kontrollálni kell.

Változó prioritású rendszer esetében egy program erőssége annak függvénye, hogy mennyit használta már a processzort. Minél tovább uralta a legfőbb erőforrást, annál gyengébbé válik és annál nagyobb lesz az esély arra, hogy a várakozó programok is szóhoz jussanak. Természetesen amikor nagyon legyengül, akkor "pihenésre" ítéli az operációs rendszer és megvárja, amíg megerősödik. (Olyan ez játék, mint a jéghokiban a csatársorok cseréje.)

Időosztásos rendszer (time-sharing) esetében az operációs rendszer egy időszeletet utal ki egy task számára és az addig fut, amíg ez le nem telt (avagy le nem mondott róla, mondjuk azért, mert I/O tevékenységbe kezdett). Az időszelet nagysága lehet előre meghatározott és dinamikusan változó, prioritásos.

Az operációs rendszerek áttekintése után térjünk vissza a memóriakezeléshez!

Particionálás:
A multitasking operációs rendszerek esetében nem megengedhető az, hogy a felhasználói programra bízzuk a memória kezelését és kihasználtságának biztosítását. A legelőször megjelent módszer a particionálás. Ennek lényege abban rejlik, hogy az operációs rendszer felosztja a memóriát és minden futó program kap egy memóriaterületet (partíciót), amely számára a felhasználható teljes memóriaterületet jelenti. Azaz az operációs rendszer annyi kis "memóriavilág"-ot szimulál, ahány program éppen fut.
A memória felosztása lehet statikus vagy dinamikus. A statikus eset lényegesen egyszerűbb, így először vizsgáljuk meg ezt!

Fix méretű partíciók módszere:

Ennél a felosztási módszernél a memóriát fix darabszámú, egyenlő vagy különböző méretű szeletekre bontják. Minden programnak legalább akkora szeletet kell kapni, hogy beleférjen. Egy olyan felosztást mutat a következő ábra, ahol 3 db kis, 2 db közepes és 1 nagyméretű program egyidejű futtatására van lehetőség.


Természetesen az egyes programok nem töltik ki a teljes, számukra kiutalt partíciót, így kérdés az, hogy mekkora partíciókat hozzunk létre? A választ erre az adott környezet (futtatandó programok mérete, gyakorisága) ismeretében lehet csak megadni és nincs általánosan alkalmazható recept. Egyensúlyt kell teremteni a leeső, nem használt memóriaterületek és a programok memóriaigénye között.
Vegyük észre, hogy egy program bármelyik olyan partícióban futtatható, amelybe belefér. Azaz ha több programot kell futtatnunk, mint ahány partíció rendelkezésünkre áll, akkor sincs gond! Megtehetjük azt, hogy egy programot ideiglenesen kipakolunk (kidobunk) a memóriából a háttértárra és a későbbiekben, amikor ismét sorra kerül visszatöltjük onnan. Ezt a folyamatot nevezzük swaping-nek, azaz cserének. A programoknak persze olyanoknak kell lenni, amelyek áthelyezhetők a memóriában, különben az egész folyamat működésképtelen.
Annak eldöntésére, hogy melyik program kerüljön sorra futásra illetve, helyhiány esetén melyik partíciót swap-eljük, különböző algoritmusok állnak rendelkezésre, melyek megpróbálják a használtság függvényében optimalizálni a rendszer kihasználtságát, igazságosan elosztani az erőforrásokat.

Változó méretű partíciók módszere:

A módszer alapötlete az, hogy a fix partícióknál keletkező (a partícióméret és a tényleges programméret közötti) kihasználatlan memóriát megszüntesse, azaz létesítsünk pontosan akkora partíciót, amekkorára szüksége van egy programnak!
Problémát egy dolog okoz, de az jó nagyot. Nevezetesen, amikor egy program kikerül a memóriából, akkor pontosan akkora üres helyet hagy hátra, mint amekkora volt (a kis piszok). Az erre a helyre bekerülő program viszont ennél kisebb lesz, azaz hasonló esettel fogunk szembenézni, mint a fix méretű partíciók esetén. Sőt ez a helyzet rosszabb, mert ott programok mindig ugyanazon a helyeken (a partíciók kezdőcímein) kezdődtek a memóriában, jelen esetben viszont ez a hely változik (időben). A gyakori programcserék miatt túl sok apró, kihasználatlan memóriadarab (lyuk) keletkezik, ezt a folyamatot a memória felaprózódásának nevezzük. Ennek megszüntetésére való a compaction (összehúzás) névre hallgató eljárás, amely a memória különböző részein levő szabad területeket egy összefüggő területté egyesíti, a program-partíciók ide-oda tologatásával. Ez a folyamat rendkívül időigényes, ezért az operációs rendszer csak abban az esetben folyamodik hozzá, ha már nagyon nagy mértékű a memória felaprózódása.
A következő ábra változó méretű partíciók esetére mutat egy példát:

(A méretek és a kezdőcímek KB-ban adottak.)

Az ábráról és a hozzá tartozó memóriatérképről jól látszik, hogy ha mondjuk egy 12 egységnyi méretű programot szeretnénk futtatni, akkor nincs elég szabad (összefüggő) memória, bár ennél jóval több a ténylegesen rendelkezésre álló szabad terület (csak nem összefüggő).
Mind a két, particiókkal dolgozó módszer tehetetlen akkor, ha olyan programot szeretnénk futtatni, amelynek mérete meghaladja a legnagyobb partíció illetve a rendelkezésre álló memória méretét.
Sőt mi több, ezek a programok nagyon rossz hatékonysággal használják ki a memóriát, mert ahhoz, hogy futásképesek legyenek, az egész programnak benn kell lenni a memóriában. Vizsgáljuk meg ezt egy picit részletesebben!

Virtuális memória:
Az alapelv a következő: egy programból annyit kell csak a memóriában feltétlenül benn tartani, hogy az végrehajtható legyen. Mivel a programok futás közben lokálisak, ezért elegendő egy mechanizmust biztosítani arra, hogy ha a program olyan memóriaterületre hivatkozik, amely nincs benn a központi memóriában, akkor az a háttértárról bekerüljön oda és a program folytathatja a végrehajtását.
Így elérhető az, hogy egy tetszőleges méretű program igen kis központi memória felhasználásával végrehajtódjon. Természetesen ezt az erőforrás-takarékosságot megfizetjük azzal, hogy a szükséges részeket a memóriába beolvassuk a háttértárról a program végrehajtása közben illetve a memóriából visszaírjuk a háttértárra a program nem használt részeit. Ez azt jelenti, hogy néhány KB memóriával elérhető az, hogy bármekkora programot végre tudjunk hajtani egy adott számítógépen (és egy pillanatra feledkezz meg a végrehajtás idejéről...)
További előnyt jelent számunkra, hogy a programok írásakor nem kell foglalkoznunk azzal, hogy az milyen környezetben, mekkora memóriájú gépen fog végrehajtódni, a programozás során úgy gondolkozhatunk, hogy egy "végtelen" nagyságú memória áll rendelkezésünkre. (A "végtelen"-t természetesen a háttértár mérete korlátozza, hiszen valahol csak kell tárolni a programunkat és az adatainkat.)
Ahhoz, hogy a program logikai címeit megfeleltessük a fizikai memóriacímeknek, szükségünk van egy leképezésre. Ezt a leképezést a memória-vezérlő (MMU: memory manager unit) valósítja meg, különböző táblázatai segítségével.
A partícionáláshoz hasonlóan itt is két módszer alakult ki: a fix méretű partíciókra hasonlító, amelyet lapozásnak, illetve a változó méretű partíciókra hasonlító, amelyet szegmentálásnak nevezünk.

Hatékonysági kérdések:
Ne feledkezzünk meg egy hatékonysági problémáról. Nevezetesen arról, hogy lapozás és szegmentálás esetén a fizikai memóriacím kiszámítása egy bonyolult leképezés segítségével történik. Azaz egy fizikai cím előállításához szükség van egy (memóriában tárolt) táblázat elemének eléréséhez illetve egy összeadásra. Azaz egy memóriacím előállítása egy újabb memóriahivatkozást tesz szükségessé.
A címszámítás sebességének növelését úgy valósítják meg, hogy a MMU rendelkezik egy asszociatív memóriával (TLB - translation lookaside buffer - címszámító buffer), amely kulcsként a lap illetve szegmens-sorszámot, értékként a hozzá tartozó memóriacímet (a lap illetve szegmens kezdőcímét) tartalmazza. Így a címszámítás során elkerülhető az, hogy minden esetben a memóriához kelljen fordulni. (Vedd észre azt, hogy a TLB működése szintén kihasználja a programok lokalitását!) A TLB általában 64-128 címet tud tárolni.

VM algoritmusok:
Az előzőekben nem beszéltünk arról, hogy a memória-kezelés folyamán milyen algoritmusok játszanak szerepet annak eldöntésében, hogy pl. melyik laptól szabaduljunk meg akkor, ha nincs szabad memória és olyan lapra történik hivatkozás, amely nincs a memóriában.
Négy tevékenységhez kell meghatározni algoritmusokat. Ezek: mennyiségi stratégia, behozási stratégia, kidobási stratégia illetve elhelyezési stratégia.

Mennyiségi stratégia: (allocation policy)
azt határozza meg, hogy egy adott programból mennyit érdemes egyidejűleg a memóriában tartani. Két véglet között kell megfelelő egyensúly találni, nevezetesen a program egészét vagy csak a lehető legkisebb részt érdemes benn tartani. Az operációs rendszer - statisztikai adatokra támaszkodva - akár időben változó algoritmus szerint határozza meg, hogy egy adott program mekkora részét érdemes benn tartani a memóriában.

Behozási stratégia: (fetch policy)
azt szabályozza, hogy mikor és mennyi lapot/szegmenst kell behozni a memóriába. Három alapvető algoritmus van:

- "mohó": annyit hoz be, amennyit csak bír;
- annyit hoz csak be amennyire éppen szükség van ("demand paging")
- a szükségeset és még néhányat

Kidobási stratégia (replacement policy)
azt határozza meg, hogy ha nincs elég memória, akkor melyik lap/szegmens kerüljön eltávolításra. Rengeteg lehetőség közül a két legfontosabb: - LRU: least recentry used - legrégebben használt kerül ki - FIFO: first in first out - a legrégebben bennlevő kerül ki.

Elhelyezési stratégia (placement policy)
azt szabályozza, hogy a háttértárról melyik szabad helyre kerüljön az adott lap/szegmens. (Lapozásnál nincs túl nagy jelentőssége, hiszen a lapok egyforma méretűek így mindegy, hogy hova kerülnek) Néhány lehetőség: - Best Fit: a lehető legkisebb helyre kerül
- Worst Fit: a lehető legnagyobb helyre kerül
- First Fit: az első olyan helyre kerül, ahova elfér
- Next Fit: ciklikus First Fit, azaz megjegyzi, hogy hol hagyta abba a keresést




Egy-két hasznos link:
A forrásoldal
Vadász-jegyzet video

Készítette: Matyikó Tamás G-128p