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