2.1. ábra: 32 bites x86-os processzorok rendszer szintű felépítése

Megszakítás- és kivétel-kezelés

A külső-, a szoftver megszakítások és a kivételek a megszakítás leíró táblán (IDT - Interrupt Description Table) keresztül vannak kezelve. Az IDT-t láthatjuk a fenti (2.1-es) ábrán. Az IDT kapu leírók kollekcióját tartalmazza, melyek lehetőséget nyújtanak a megszakítás illetve kivétel kezelésre. Ahogy a GDT, úgy az IDT se szegmens (ugyanis nincs leírója, s ezáltal kiválasztója se). Az IDTR (IDT regiszter) tartalmazza a lineáris kezdőcímét illetve a hosszát.

Az IDT-ben elhelyezkedő leírók lehetnek mind megszakítás, csapda (tarp), vagy taszkkapu típusúak. Ahhoz, hogy elérjük a megszakítást illetve a kivételt, a processzornak először a megszakítás vektort (megszakítás számot) kell fogadnia a belső hardvertől, vagy külső megszakítás vezérlőtől, vagy a programból a szoftver megszakítást, amit INT, INTO, INT 3 vagy BOUND parancsokkal adhatunk ki. Így a megszakítás vektor egy indexet fog jelenteni az IDT-ben egy kapu leíróra. Ha a kiválasztott kapu leíró egy megszakítás vagy csapda (trap) kapu akkor, a párosított kezelő rutin (amit a hívott indexű pl. megszakítás) a call kapun keresztüli meghíváshoz szinte teljesen hasonlóan fut le. Ha a leíró taszk kapu volt, akkor a kiszolgáló rutin egy taszk átváltáson keresztül fog végrehajtódni.

 

Memóriakezelés

A processzor támogatja mind a közvetlen fizikai illetve mind a virtuális (lapozás, paging által történő) memóriakezelést. Amikor fizikai címzést használunk, akkor a lineáris címmel a processzor úgy bánik, mint a fizikaival: egy az egybe. (Fontos: ne felejtsük el, hogy nem közvetlenül a memóriát címezzük, hanem a memória modellt, s ez a memória modell fogja a lineáris címet fizikaiként értelmezni, s csak ezen keresztül érjük el a memóriát. A védett módú memóriakezelés egy egész fejezetet kitesz, így ezzel részletesen csak később foglalkozunk.) Így, ha lapozást használunk (lapozás, paging: a memória területet szempontok alapján lapokra, egységekre bontják, ez segíti elő a virtuális memóriakezelést), tehát ha lapozást használunk, akkor akár az összes kód-, adat-, verem-, illetve rendszerszegmenst, a GDT-t és az IDT-t is lapozhatjuk (ezáltal háttértárolóra lementhetjük), s ezáltal csak a legfontosabb lapok maradnak bent a fizikai memóriában, míg a többi amíg nem használjuk, a HDD-n tárolódik.

A lapokat, pontosabban fogalmazva a "lap kereteket" (pages; page frames) a fizikai memóriában két (bizonyos esetben látni fogjuk a Pentium Pro-tól kezdve lehet három) különböző rendszer típusban találhatjuk meg: a lapcímtárakban (page directory) és laptáblázatok (page table) összességében. Mind kettőjük valahol a memóriában van, ezt a fenti ábra jól mutatja. A lapcímtár olyan elemekből épül fel, melyek tartalmazzák egy laptáblázat kezdődő fizikai címét, hozzáférési jogait, és memóriakezelési információkat. A laptáblázat elemei pedig az elérni kívánt lapnak (ugyanis a memória teljesen lapokra van osztva) a kezdő fizikai címét adja meg. Ehhez adjuk az ofszetet, s el is érhetjük a kívánt bájtot. (A lapozást a védett módú memóriakezelés fejezetben részletezzük.). A lapcímtárnak a fizikai kezdő címe a CR3 vezérlő regiszterben (PDBR) van.

Ahhoz, hogy ezt a lapozási mechanizmust használhassuk, a lineáris címet három részre osztották az architektúra tervezői, így szolgáltatva különálló ofszetet a lapcímtárba, a laptáblázatba és a lapkereten belül.

A rendszer állhat egy lapos lapcímtárból, vagy több laposból. Pl. minden taszknak lehet saját lapcímtára. Ez nyílván való, hisz a CR3 regiszter a TSS-ben el van mentve, és így taszk váltáskor megadhatunk új CR3-at, ezáltal új lapcímtárat.

Rendszer regiszterek

Részint a processzort támogatják initializálásban, illetve az operációs rendszert segítik kontrollálni az alább tárgyalt FLAG illetve rendszer regiszterek.

  • A rendszer flagek (Mi az a flag? Egy vagy pár bitből álló jelző vagy vezérlő bit(cspoport). Ha ez illetve ezek a bitek be vannak kapcsolva, akkor jelzik, hogy ezt lehet vagy, nem lehet csinálni. Előfordul, hogy több biten vannak pl. számok letárolva, ilyen az IOPL, két bites. 0-3-ig számokat tartalmazhat melyek, amit később tárgyaljuk a privilégium szintet tárolják.), tehát a rendszer flagek, illetve az IOPL az EFLAGS regiszterben kontrollálják a taszk váltást, a mód váltást (valós - védett - virtuális valós), megszakítás kezelést, nyomkövetést - debugging, és a hozzáférési jogokat. Ezekről az EFLAGS regiszter tárgyalásakor alább ejtünk szót.
  • A vezérlő regiszterek (CR - Controll Register), CR0, CR2, CR3 és CR4. Ezek a regiszterek meglehetősen sok rendszer szintű flaget, illetve adat mezőt tartalmaznak a rendszer szintű utasítások megvalósítása érdekében. Ezekről a vezérlő regisztereket tárgyaló részben beszélünk bővebben. (Hogy miért maradt ki a CR1? Ugyanis nem létezik, pontosabban fenntartott a későbbi fejlesztésekre.)
  • Léteznek specifikus debug ("hibakereső") regiszterek is.
  • A már említett GDTR, LDTR, illetve IDTR regiszterek a védett memória-kezelés leíró táblázatainak kezdő címét illetve méretét tartalmazza. Ezekről az EFLAGS regiszter után beszélünk.
  • A TR (Task Register) melyről már szót ejtettünk, az aktuális TSS lineáris kezdő címét illetve méretét tartalmazza. Hasonlóan az EFLAGS után tárgyaljuk.
  • Vannak még az egyenlőre számunkra nem oly fontos modell (processzor) specifikus regiszterek (MSRs - Model-Specific Register) melyek csak az operációs rendszer kernel-je által hozzá férhető, s leginkább csak számukra hordoz információt.

Más rendszer erőforrások

Az előbb említetteken kívül vannak még más nem tárgyalt rendszer regiszterek, illetve adat struktúrák melyek az alábbi rendszer feladatokat látják el:

  • Operációs rendszeri utasítások (LGTD, SGDT ...)
  • Teljesítmény, működés megfigyelő rendszer elemek, MSR függő. (Preformance-monitoring counters)
  • Belső cache és bufferek. L1 cache, TLB illetve az L2 cache kezelése. (Internal cache and buffers)

 

A processzor működési módjai

Az Intel által kifejlesztett, szabványos 32 bites x86-os architektúra a három alábbi működési módot illetve egy kvázi módot támogat:

  • Védett mód (Protected mode): Ez a processzor eredeti működési módja. Ebben az üzemmódban használható az összes utasítás illetve architektúrai lehetőség, újítás. Ezen üzemmódban használhatja ki programunk leghatékonyabban a processzort, így ez a mód javasolt minden program illetve operációs rendszer számára.
  • Valós (címzésű) mód (Real-address mode): Ez az működési mód ez eredeti Intel 8086 processzor környezetét adja nekünk. Természetesen a csak valós módot használó programok nem lettek hatékonyabbak, mint egy 8086-os processzoron, csak gyorsabbak hisz ma, akár 2.2 GHz-cel is működhet egy iP4 valós módban. Ez a mód tartalmaz annyi kiegészítést, hogy használhatóak a valós módban is érvényes 286, 386, 486, illetve Pentiumos utasítások. Ilyen lehet a 32 bites operandusú műveletek, pl.: ADD EAX, EBX, stb. Vagy a 486-os BSWAP. Továbbá a legfontosabb a 386+-tól ismert módon a védett módba váltás lehetősége.
  • Rendszer menedzselő mód (SMM - System management mode): A rendszer menedzselő mód (SMM) már egy szabványosított lehetőség a 32 bites Intel Architektúrájú processzoroktól, az Intel386TM SL-től kezdődően. Ez adja meg az operációs rendszereknek azt a lehetőséget, hogy hasznosan, aktívan alkalmazzák az energia gazdálkodást. Ez annyit tesz, ha adott ideig "nem csinálunk semmit" akkor, elmenti az aktuális regiszterek értékét, és egy olyan módba kapcsol ahol jelentősen kisebb az órajel s csak arra figyel, hogy jön -e a visszatérésre, a munkába való visszakapcsolásra a jel. Ebbe a módba (SMM-be) külső megszakítás jel által kerülhet a processzor, ez az SMI# és ez fog rendszer menedzselő megszakítást (system management interrupt) generálni. Erre a processzor egy elkülönített címzési területre lép, míg el nem menti az éppen futó taszk adatait. SMM-ből való visszatérés után az SMI bekövetkezése előtti állapot áll helyre.
  • Viruális 8086-os mód (Virtual-8086 mode): Ez nem egy létező - különálló működési mód, hanem egy kvázi üzemmód. Ez egy valós programot egy védett - multitaszkos környezetben tud futtatni. Ennek az a jelentősége, hogy a valós program számára az operációs rendszer egy virtuális, látszólagos valós módot szimulál. A valós program ebből a környezetből annyit lát, mintha tényleg csak az övé lenne az egész processzor, holott a védett operációs rendszer felügyelete alatt áll. Ezt a lehetőséget biztosítja rendszer szinten a processzor az operációs rendszernek.

2.2. ábra: A processzor működési módjai közötti átjárás

A processzor bekapcsolás vagy reszet után valós módba kerül. Védett módba a PE flag egyre állításával kerülhetünk. Ez a flag a CR0 regiszter 0. bitje. Hasonló analógia alapján ezen bit 0-ra állításával kerülhetünk vissza valós módba. (Lásd 2.2-es ábra.) Mielőtt a tisztelt Olvasó rögtön rohanna ezen bitet 1-re állítani meg kell jegyezni, hogy ez nem ily egyszerű. Ugyanis rengeteg körülményt kell megteremteni ahhoz, hogy átválthassunk védett módba, e körülmények nélkül gyönyörű reszetet vagy lefagyást kreálunk. A védett módba kapcsolás, illetve ennek példaprogramja egy másik fejezet témája.

A következő kérdés, hogy virtuális 8086-os módba hogyan kerülhetünk. Nyílván védett módban kell lennünk, hogy virtuális 8086-osba kerülhessünk. Ennek megvalósítása a már sokat emlegetett EFLAGS regiszter VM flagjével oldható meg. (Hasonlóan, ha 1-be vált és adottak a feltételek, akkor lép virtuális 8086-ba illetve ha 0-ba, akkor onnan ki.) (Lásd ábra.) A processzor megtervezésének szépsége az, hogy taszk váltáskor a TSS-ben az ELFAGS regiszter is benne van, így ha a másik taszknak virtuális 8086-os kvázi üzemmódban kell mennie akkor ez automatikusan megtörténik. (Amikor ilyen taszkra váltunk akkor az EFLAGS által bekapcsolódik, illetve amikor innen elváltunk akkor pedig kikapcsolódik.)

Megjegyzem, hogy az SMM-ből való visszatérés után nyilvánvaló, hogy olyan módba tér vissza mely az SMI bekövetkezése előtt volt. (RSM által tér vissza, amit az ábra is mutatja.)

 

Az EFLAGS regiszter felépítése

A 32 bites EFLAGS regiszter egy csoport állapot flaget (jelző bitet), egy csoport vezérlő bitet, és egy csoport rendszer bitet tartalmaz. Az alábbi (2.2-es) ábra definiálja az egyes jelzőbitek helyét a regiszterben. Az inicializációs folyamat után az EFLAGS regiszter értéke 00000002h. Az 1, 3, 5, 15, 22 és 31 közötti bitek fenntartottak, így azok megváltoztatása, programból történő használata nem javasolt.

Az EFLAGS regiszter egyes flagjei közvetlen módon állíthatóak az alább ismertetett speciális utasításokkal. Nincs olyan utasítás melynek segítségével közvetlenül az egész regiszter tartalmát vizsgálhatnánk, vagy átállíthatnánk. Azonban az alábbi trükkel, megoldható a regiszter értékének tetszőleges állítása. Az alábbi utasítások lehetővé teszik, hogy adott flageket a program rutinjának vermébe vagy az EAX regiszterbe írjunk, illetve fordítva, vagy a veremből illetve az EAX regiszterből írjuk az EFLAGS regiszterbe. Az utasítások a következőek: LAHF, SAHF, PUSHF, PUSHFD, POPF, és POPFD. Miután az EAX regiszter tartalmát a verembe vagy az EAX regiszterbe mentettük lehetőség nyílik, hogy a processzor bit manipuláló utasításaival állítsuk át a flageket (BT, BTS, BTR, és BTC utasítások).

Amikor az egyik taszk futását megszakítjuk, akkor a processzor automatikusan elmenti az EFLAGS regiszter értékét a taszk állapot szegmensbe (TSS). Amikor vissza kívánunk térni a megszakított taszkhoz, akkor a processzor egyszerűen visszaírja az EFLAGS regiszterbe a TSS-ben elmentett értéket.

Amikor egy megszakítás vagy kivétel meghívódik, a processzor automatikusan elmenti a rutin verem területén az EFLAGS regiszter értékét. Amikor a megszakítás vagy kivétel lekezelése taszkváltással jár, akkor természetesen az aktuális EFLAGS regiszter értéke a TSS-ben lesz eltárolva. Lássuk az EFLAGS regiszter felépítésének ábráját:

2.3. ábra: Az EFLAGS regiszter felépítése

A 32 bites x86-os architektúra fejlődésével mindig újabb és újabb flagek adódtak az először csak 16 bites FLAGS regiszterhez, majd az Intel386-tól 32 bitesnek definiált EFLAGS regiszterhez. A kompatibilitás megőrzése végett természetesen egy előző processzorcsaládban definiált flagek a következőben sem változnak meg. Fontos továbbá, hogy a fenntartottnak jelzett biteket ne használhatjuk programainkban, hisz ezek funkciója bármikor megváltozhat.

  • Bit 0: Átvitel flag (CF - Carry Flag): ez akkor 1, ha akármilyen aritmetikai művelet eredménye nem volt ábrázolható az adott adat struktúrán, pl: MOV AX, FFFFh ; ADD AX, 2. Így ekkor a CF flag egy a legfelső fölötti bitként szolgál. (Csak akkor használt ha elég az egy bitnyi kiterjesztés.) A CF értéke állítható a POPF, POPFD, IRET utasításokkal a megfelelő módon, illetve egyszerűbb a CLC (CF=0), vagy CMC (CF= NOT CF), vagy STC (CF=1).
  • Bit 1: nem használt, fenntartott
  • Bit 2: Párosságot jelző flag (PF - Parity Flag): Ha az utolsó aritmetikai művelet eredménye páros, akkor 1.
  • Bit 3: nem használt, fenntartott
  • Bit 4: Fél-átvitel flag (AF - Adjust Flag): értéke egy ha BCD aritmetikai művelet után a 3. bit felett átvitel illetve alulcsordulás volt
  • Bit 5: nem használt, fenntartott
  • Bit 6: Nulla flag (ZF - Zero Flag): Ha egy művelet eredmény nulla volt
  • Bit 7: Előjel flag (SF - Sign Flag): Előjeles, egész számok használata esetén: jelentése egyenértékű a legnagyobb helyiértékű bittel ami nem más, mint az előjel bit. Tehát az elvégzett művelet eredményének előjelét adja meg, ha az előjeles egész szám volt.
  • Bit 8: Csapda flag (TF - Trap Flag): Engedélyezi a lépésenkénti végrehajtást. Ez úgy valósul meg, hogy minden utasítás után egy debug kivételt generál, így megfigyelhetjük mi történt a végrehajtott utasítás után. Ezt beállítani a POPF - PUSHF illetve IRET utasításokkal lehet. Ha egyre állítottuk ezen bitet, rögtön debug kivétel képződik.
  • Bit 9: Megszakítás flag (IF - Interrupt Flag): Itt lehet állítani a maszkolható megszakítások engedélyezését. (Mi az, hogy maszkolható megszakítás? A megszakítás fogalmát ismertnek tekintjük, így: a megszakítások két részre bonthatók a maszkolhatókra illetve a nem maszkolhatóakra. A nem maszkolható megszakítás (NMI) olyan eseményt jelez a processzornak ami nem tűr halasztást, pl. memória, vagy periféria hiba. A maszkolható megszakítás pedig hardver által kezdeményezett megszakítás. Mivel ezek maszkolhatóak, egy megszakítás várakozási sorba kerül, s jelentőssége, prioritása szerint lesz sorba helyezve. Így ha fontosabb, akkor az előbb beérkezett megszakítás előtt is végrehajtódhat. Nos ezzel a bittel az utóbb említett maszkolható megszakítások engedélyezését lehet beállítani.) Nos általában ez a bit engedélyezett, csak akkor szoktuk letiltani, ha épp pl.: egy saját szoftver megszakítást - egy ilyen rutint futtatunk. Ezen rutint nem szakíthatják félbe egyéb maszkolható megszakítások, mert számos probléma lehetne belőle. Azt, hogy ez a bit módosítható -e az éppen futó taszk - program számára azt a alább tárgyalt IOPL, CPL, illetve VME flag (VME CR4-ben) együttes hatása dönti el. A módosítás a CLI (nem engedélyezi a maszkolható megszakításokat), STI (engedélyezi), POPF, POPFD vagy IRET által beállítandó (E)FLAG regiszter ezen adott bitjének értéke szerint.
  • Bit 10: Irány flag (DF - Direction Flag): A string (karakterlánc, bájtsorozat) műveletek (MOVS, CMPS, SCAS, LODS, és STOS) irányát állítja. Ha DF = 1 akkor automatikusan csökken a cím érték (ez a magasabb címtől alacsonyabb felé haladás), ha DF = 0 akkor automatikus növekedés (alacsonyabb címtől a magasabb felé). Az STD (ez állítja DF = 1) illetve CLD (ez állítja DF = 0) parancsokkal illetve a már említett POPF, POPFD és IRET utasításokkal állítható be a bit értéke.
  • Bit 11: Túlcsordulás flag (OF - Overflow Flag): Ha a művelet eredménye túl nagy pozitív szám, vagy túl kicsi negatív akkor értéke = 1, egyébként 0.
  • Bit 12 - Bit 13: I/O privilégium szint mező (I/O privilege level field): Az éppen futó taszk vagy program I/O privilégium szintjét (IOPL) adja meg. A CPL (CPL - Current Privilege Level; aktuális privilégium szint), tehát a CPL-nek kisebb vagy egyenlőnek kell lennie az IOPL-lel, hogy a taszk vagy program hozzáférjen a I/O porthoz. (Kisebb vagy egyenlő CPL jelenti, hogy magasabb, a kernelhez közelebbi szinten van a taszk vagy program.) Ez IOPL értékét csak a kernel (CPL = 0) tudja állítani a POPF vagy IRET utasítással.
  • Bit 14: Beágyazott taszk flag (NT - Nested task flag): Meghatározza a vagy megszakítás, vagy call által meghívott taszkok változását. Ha CALL vagy megszakítás, vagy kivétel miatt került meghívásra az új taszk akkor a processzor az NT flaget 1-re állítja, s elmenti a TSS előző taszk mutató mezőjébe (previous task link) az elhagyandó taszk TSS kiválasztóját. Ha az új taszkból IRET-tel lép ki a program és az NT flag = 1 akkor, a processzor az elmentett TSS kiválasztót tölti a TR-be (taszk regiszter), és azt futtatja. Ez addig folyatódik, míg a legfelső szintű taszkhoz nem érünk el ahol az NT = 0. Ezt lehet egy zárt láncra is alkalmazni, de a pre-emptiv operációs rendszerek általában nem hagyják szépen lassan a teljes programot lefutni, illetve nem várják meg az IRET-et, mert addig a többi taszk nem tudná végezni munkáját, hanem az időosztás elve szerint mindegyik taszk a prioritásának megfelelően fut kicsit majd átvált egy másik taszkra. Azonban ha JMP-vel ugrottunk a taszkra, akkor az NT flag értékét a processzor 0-ra állítja, ezáltal megszüntetve a láncot. Az NT flag értékét a POF, POPFD vagy IRET utasítással állíthatjuk át, ugyanakkor ezt körültekintéssel tegyük, mert váratlan kivételt (unexcepted exception) okozhat az alkalmazásban. Alábbi ábránk az egymásba ágyazott taszkokra mutat példát:

2.4. ábra: Egymásba ágyazott végrehajtású taszkok

  • Bit 15: nem használt, fenntartott
  • Bit 16: Folytatás flag (RF - Resume Flag): Ha beállított, átmenetileg letiltja a lépésenkénti végrehajtásra szolgáló debug kivétel (#DE - debug exception) generálását. Ha 0, akkor minden utasítás után generálható debug kivétel. Az elsődleges funkciója az utasítás újra végrehajtása (folytatása) - pl. hiba esetén - anélkül, hogy újabb #DE-t okozna a végrehajtáshoz szükséges IRETD utasítás.
  • Bit 17: Virtuális 8086-os mód (VM - Virtual-8086 mode): Ennek egyre állításával léphetünk virtuális 8086-os módba ha a processzor már védett módban van és megfelelőek a körülmények. Ennek 0-ra állításával e módból a sima valósba visszalépés történik meg.
  • Bit 18: Illeszkedés ellenőrzés (AC - Alignment check): Ennek és a CR0-ban lévő AM flagnek az 1-re állításával kapcsolhatjuk be a memória referenciák ellenőrzését. Ennek és/vagy a CR0.AM-nek a 0-ra állításával az illeszkedés ellenőrzés letiltás állítható be. Kivétel képződik ha egy word cím nem páros (osztható kettővel), vagy egy doubleword cím nem osztható néggyel. A kivétel csak a felhasználói programokat érinti (amelyeknek a privilégium szintje = 3). Az ilyen jellegű csoportosítás főként a multi-processzoros rendszerekben: két processzor közötti adat cserekor, vagy speciális pointer-táblázatok kezelésekor nyújt könnyedséget.
  • Bit 19: Virtuális megszakítás (VIF - Virtual Interrupt Flag): A már tárgyalt IF flag virtuális mását tartalmazza. A VIF flag a következő VIP flaggel együtt használt. A processzor a futtatásban akkor veszi csak figyelembe a VIF flaget, ha a CR4-ben a VME vagy a PVI flag egyre állított és az IOPL kisebb, mint 3.
  • Bit 20: Virtuális megszakítás-felfüggesztés (Virtual interrupt pending): A szoftver által beállított flag melynek jelentése, hogy egy megszakítás függőben van. Ha 0, akkor nincs függőben lévő megszakítás. Ahogy előbb említettük, a VIF flaggel együtt használt, s ugyanúgy vonatkozik rá, hogy: az IOPL-nek kisebb vagy egyenlőnek kell lennie 3-mal, illetve a CR4-ben vagy a VME vagy PVI biteknek bekapcsoltnak kell lenniük. A VIP flaget a processzor mindig csak kiolvassa, de nem módosítja.
  • Bit 21: Azonosítás (ID - Identification): Az újabb processzorok rendelkeznek azzal a lehetőséggel, hogy információt adjanak típusukról, gyártójukról, illetve az általuk támogatott speciális műveletekről. Ez a bit arra szolgál, hogy megtudjuk rendelkezik -e a processzor lekérdezhető információval. Ha értéke egy, akkor támogatja a CPUID utasítást, ellenkező esetben nem.
  • Bit 22 - Bit 31: nem használt, fenntartott