Řízení operační paměti Okruh 8 · Architektura počítačů

Řízení operační paměti

Hluboké porozumění strategiím správy paměti — od základních principů po pokročilé algoritmy výměny stránek.

§1

K čemu slouží operační paměť a co se v ní nachází

Operační paměť (RAM — Random Access Memory) je srdcem každého počítače. Bez ní by procesor neměl kde uchovávat instrukce ani data, se kterými právě pracuje.

Představte si počítač jako pracovní stůl a operační paměť jako jeho plochu. Pevný disk (nebo SSD) je jako velká skříň v kanceláři — pojme obrovské množství věcí, ale než s čímkoliv začnete pracovat, musíte to nejprve položit na stůl. Operační paměť je právě ten stůl: omezená plochou, ale zato bleskurychlá a přímo dostupná.

Technicky vzato slouží operační paměť jako dočasné úložiště pro vše, co procesor aktivně potřebuje. Nacházejí se v ní:

Kód spuštěných programů — instrukce, které procesor čte a vykonává jeden za druhým. Program nelze spustit z disku přímo; musí být nejprve nahrán do RAM. Data programů — proměnné, zásobník (stack), halda (heap), otevřené soubory. Každý program potřebuje prostor pro vlastní data. Kód a data operačního systému — jádro OS (kernel) musí být vždy přítomno v paměti, protože spravuje veškeré prostředky počítače a obsluhuje požadavky programů. Mezipaměti a vyrovnávací buffery — OS si v RAM ukládá nedávno načtené bloky z disku, aby je nemusel znovu číst.

Klíčový princip
Procesor pracuje výhradně s daty v RAM (a v registrech/cache). Vše ostatní — disk, síť — musí být nejprve do RAM přeneseno. Rychlost RAM je proto kritická: moderní DDR5 dosahuje desítek GB/s, zatímco přístup na SSD je stokrát pomalejší.

Protože RAM je omezená a programů bývá mnohem víc, než by se do ní všechny najednou vešly, musí operační systém velmi pečlivě řídit, kdo dostane kolik paměti, kde přesně v adresním prostoru, a co se stane, když paměti začíná ubývat. Celá tato disciplína se nazývá správa paměti (memory management).

Základní problém, který musí správa paměti řešit, je izolace: každý proces musí mít pocit, že má paměť pro sebe, a nesmí číst ani zapisovat do paměti jiného procesu. Zároveň je třeba efektivita: paměť nesmí být zbytečně fragmentovaná ani plýtvaná. A konečně virtualizace: procesy by ideálně neměly vůbec vědět, kde fyzicky v RAM jejich data leží.

§2

Strategie přidělování operační paměti

Jak přidělit paměť procesům? Existuje několik zásadně odlišných přístupů, z nichž každý má svá specifická silná místa i slabiny. Projdeme je od nejjednoduššího k nejsofistikovanějšímu.

Přidělování souvislé oblasti

Nejpřímočařejší přístup: každý proces dostane jeden nepřerušený kus fyzické paměti. Jako byste na pracovním stole přidělili každému kolegovi vlastní ohraničenou část plochy — nikdo nesmí překračovat svou hranici.

V tomto modelu je fyzická paměť rozdělena na dvě části: oblast pro operační systém (typicky na nízkých adresách) a oblast pro uživatelské procesy. Každý proces dostane při spuštění jeden blok paměti. Procesor obsahuje dva speciální registry: base register (bázový registr), který uchovává počáteční fyzickou adresu přiděleného bloku, a limit register (mezní registr), který říká, jak je blok velký. Při každém přístupu do paměti hardware automaticky kontroluje, zda logická adresa procesu nepřekračuje mezní hodnotu — pokud ano, vyvolá výjimku (tzv. segmentation fault).

Přidělení souvislého bloku paměti
Obr. 1a — Přidělení souvislé oblasti. Fyzická RAM je rozdělena na část pro OS a část pro uživatelské procesy. Každý proces zaujímá jeden nepřetržitý blok. Bázový registr určuje, kde blok začíná; mezní registr hlídá, aby proces nevystoupil ze svého prostoru. Adresní překlad je triviální: fyzická adresa = logická adresa + base.
Přidělení souvislého bloku — detail
Obr. 1b — Detail překladu adres při souvislém přidělování. Logická adresa procesu se přičte k hodnotě bázového registru. Zároveň se porovná s mezním registrem — pokud je větší nebo rovna, dojde k chybě přístupu (trap). Tento hardwarový mechanismus zajišťuje ochranu paměti.

Výhodou je absolutní jednoduchost — překlad adresy je jedna operace sčítání. Nevýhody jsou však závažné. Za prvé: jak zjistit, kam nový proces umístit? Paměť obsahuje volné díry různých velikostí a my musíme najít vhodnou. Existují tři klasické strategie výběru díry:

First Fit (první vhodná) — projdeme seznam volných bloků a vybereme první, který je dostatečně velký. Je to rychlé, ale tenduje k tomu, že velké bloky na začátku paměti se roztříštějí na malé kousky.

Best Fit (nejlepší vhodná) — vybereme nejmenší blok, který je stále dostatečně velký. Zdánlivě šetří paměť, ale ve skutečnosti vytváří spoustu malých zbytků, které nejsou použitelné pro nikoho.

Worst Fit (nejhorší vhodná) — naopak vybereme největší dostupný blok. Logika je taková, že zbývající kus bude stále dost velký pro další proces. V praxi ale funguje nejhůř.

Druhý zásadní problém je fragmentace. Jak procesy přicházejí a odcházejí, v paměti vznikají díry. I kdyby celkového volného místa bylo dost, jednotlivé díry mohou být příliš malé pro nový velký proces — tato situace se nazývá externí fragmentace. Řešením je kompaktace (defragmentace) — přesun procesů v paměti tak, aby se volné bloky sloučily. To je ale extrémně časově náročné a vyžaduje zastavení procesů.

Omezení přístupu souvislé oblasti
Přidělování souvislé oblasti trpí těžkou externí fragmentací. Reálné operační systémy ho v čisté podobě nepoužívají — přešly k sofistikovanějším metodám. Historicky se používalo v jednoduchých systémech jako MS-DOS.

Přidělování paměti po blocích (segmentech/sekcích)

Místo jednoho velkého bloku lze paměť přidělovat po menších, ale stále fyzicky souvislých blocích. Myšlenka je prostá: rozdělíme fyzickou RAM na bloky pevné nebo proměnné velikosti a procesy dostávají tolik bloků, kolik potřebují — nemusí být všechny pohromadě.

Přidělování paměti po sekcích
Obr. 2a — Přidělování paměti po sekcích (blocích). Fyzická RAM je rozdělena do bloků. Každý proces může obdržet více bloků, které nemusí být fyzicky sousední. OS udržuje tabulku, která mapuje logický pohled procesu na fyzické umístění jednotlivých bloků. To výrazně snižuje externí fragmentaci.
Dynamické přidělování paměti
Obr. 2b — Dynamické přidělování paměti. Ukazuje, jak se paměť postupně zaplňuje a uvolňuje při dynamickém přidělování. Vznikají fragmenty různých velikostí. Algoritmy jako First Fit, Best Fit a Worst Fit se snaží minimalizovat plýtvání, každý s jiným přístupem k výběru vhodného volného bloku.

Přidělování po blocích je přechodem k virtualizaci paměti: proces vidí svůj logický adresní prostor jako souvislý, ale ve skutečnosti je rozložen po více fyzicky oddělených oblastech. OS si udržuje datovou strukturu (tabulku bloků), která říká: „logický blok č. 0 tohoto procesu leží na fyzické adrese XYZ".

Tento přístup výrazně snižuje externí fragmentaci — místo jedné velké díry stačí mít několik menších. Stále však existuje interní fragmentace: pokud jsou bloky pevné velikosti a proces potřebuje méně než jeden blok, zbytek se plýtvá.


Stránkování (Paging)

Stránkování je pravděpodobně nejdůležitější technika správy paměti v moderních operačních systémech. Základní idea elegantně řeší problém fragmentace: rozdělíme jak logický adresní prostor procesu, tak fyzickou paměť na bloky pevné velikosti. Bloky logického prostoru se nazývají stránky (pages), bloky fyzické paměti se nazývají rámce (frames nebo page frames). Velikost stránky a rámce je vždy stejná — typicky 4 KB, ale existují i větší (2 MB, 1 GB — tzv. huge pages).

Klíčová myšlenka: stránky nemusejí být v rámcích umístěny ve stejném pořadí, v jakém jsou v logickém prostoru. Logická stránka 0 může být ve fyzickém rámci č. 17, logická stránka 1 v rámci č. 3, atd. A rámce různých procesů mohou být libovolně promíseny v paměti — každý proces má svou vlastní tabulku stránek a vůbec neví, kde fyzicky jeho data jsou.

Princip stránkování paměti
Obr. 3a — Princip stránkování paměti. Logický adresní prostor (vlevo) je rozdělen na stránky (P0, P1, P2...). Fyzická RAM (vpravo) je rozdělena na rámce. Tabulka stránek (uprostřed) mapuje každé číslo logické stránky na číslo fyzického rámce. Všimněte si, že stránky procesu jsou v rámcích rozmístěny nepravidelně — pro proces je to však transparentní.

Překlad logické adresy na fyzickou

Logická adresa je vnitřně rozdělena na dvě části: číslo stránky (page number, značíme p) a offset (přesun v rámci stránky, značíme d). Pokud je velikost stránky 2ⁿ bajtů, pak spodních n bitů logické adresy tvoří offset a zbývající vyšší bity tvoří číslo stránky.

Logická adresa = [číslo stránky p | offset d] Fyzická adresa = rámec[p] × velikost_stránky + d

Kde rámec[p] je číslo fyzického rámce, které zjistíme z tabulky stránek pro daný proces. Offset d zůstává beze změny — v rámci je struktura dat stejná jako ve stránce. Překlad provádí hardware nazývaný MMU (Memory Management Unit) transparentně při každém přístupu do paměti.

Stránkování — detailní schéma překladu
Obr. 3a2 — Detailní schéma překladu adres při stránkování. CPU generuje logickou adresu. MMU ji rozdělí na číslo stránky (p) a offset (d). Indexem p vstoupí do tabulky stránek daného procesu a získá číslo fyzického rámce (f). Výsledná fyzická adresa je f konkatenováno s d. Celá operace trvá jen několik nanosekund díky hardwarové implementaci.

Translation Lookaside Buffer (TLB)

Tabulka stránek je uložena v RAM, ale každý přístup do paměti by pak vyžadoval dva přístupy do RAM (nejprve tabulka stránek, pak vlastní data) — to by bylo katastrofálně pomalé. Proto procesory obsahují speciální rychlou asociativní cache nazývanou TLB (Translation Lookaside Buffer). TLB si pamatuje posledně použité překlady (číslo stránky → číslo rámce). Pokud v ní hledaná stránka je (TLB hit), překlad proběhne v jednom cyklu bez přístupu do RAM. Teprve při TLB miss se musí sáhnout do tabulky stránek v RAM.

TLB — Translation Lookaside Buffer
Obr. 3b — Animace překladu adres přes TLB. CPU nejprve hledá číslo stránky v TLB (malá, rychlá asociativní paměť). Při TLB hit se fyzická adresa sestaví okamžitě. Při TLB miss se sáhne do tabulky stránek v RAM, překlad se uloží do TLB pro příštím použití a pak se pokračuje. Díky principu lokality přístupů (programy typicky přistupují stále ke stejným stránkám) je TLB hit rate velmi vysoký — typicky 95–99 %.

Hierarchická tabulka stránek a inverzní tabulka

64bitový adresní prostor je enormní — naivní jednoduchá tabulka stránek by pro každý proces zabrala gigabajty. Proto se používají víceúrovňové tabulky stránek (dvou- nebo třístupňové). Logická adresa je rozdělena na více polí — každé pole indexuje do jedné úrovně tabulky. Části tabulky, které odpovídají nepouživaným oblastem adresního prostoru, vůbec neexistují — ušetří se tak obrovské množství paměti.

Alternativou je inverzní tabulka stránek (inverted page table): místo tabulky pro každý proces existuje jediná globální tabulka, která má jeden záznam pro každý fyzický rámec. Záznam říká, které procesu a které jeho stránce rámec patří. Je paměťově úsporná, ale vyhledávání v ní je pomalejší (musíme hashovat).

Proč je stránkování tak mocné?
Stránkování zcela eliminuje externí fragmentaci — každý volný rámec může být přidělen komukoli, bez ohledu na sousedství. Interní fragmentace je minimální (maximálně jedna stránka na konci každého procesu). Navíc umožňuje virtuální paměť: stránky, které se nevejdou do RAM, lze odkládat na disk.

Segmentace

Zatímco stránkování rozděluje paměť na bloky pevné velikosti bez ohledu na logickou strukturu programu, segmentace vychází z toho, jak programátor přirozeně přemýšlí o programu. Program se skládá ze segmentů: kód (text), datová sekce, zásobník, halda, sdílené knihovny atd. Segmentace tyto logické celky přímo mapuje na bloky fyzické paměti.

Každý segment má své vlastní jméno (nebo číslo) a délku. Logická adresa v segmentovaném systému se skládá ze dvou částí: číslo segmentu a offset v rámci segmentu. OS udržuje pro každý proces tabulku segmentů, která říká, kde fyzicky každý segment začíná (base) a jak je velký (limit).

Logická adresa = [číslo segmentu s | offset d] Fyzická adresa = tabulka_segmentů[s].base + d Podmínka: d < tabulka_segmentů[s].limit (jinak výjimka!)
Segmentace — principiální schéma
Obr. 4a — Principiální schéma segmentace. Logický adresní prostor procesu je rozdělen na pojmenované segmenty (kód, data, zásobník...). Každý segment je namapován na jinou oblast fyzické RAM. Adresní překlad probíhá pomocí tabulky segmentů: číslo segmentu určí řádek tabulky, z něj se vezme bázová adresa a přičte se offset. Pokud offset překročí délku segmentu, nastane chyba.
Segmentace — fyzické rozmístění
Obr. 4b — Fyzické rozmístění segmentů v paměti. Segmenty různých procesů jsou rozmístěny v fyzické RAM nepravidelně. Každý segment je fyzicky souvislý, ale mezi segmenty mohou být díry. Segmenty mohou mít různou velikost a mohou dynamicky růst (např. zásobník se zvětšuje při volání funkcí). Vidíme, že různé procesy mohou sdílet jeden fyzický segment (např. sdílenou knihovnu).
Segmentace — příklad překladu adresy
Obr. 4c — Příklad překladu adresy při segmentaci. Konkrétní logická adresa (číslo segmentu + offset) je přeložena pomocí tabulky segmentů na fyzickou adresu. Zároveň se ověřuje, že offset nepřekračuje délku segmentu — ochranný mechanismus, který brání přístupu mimo alokovanou oblast.

Hardwarová podpora segmentace

Překlad segmentových adres musí být rychlý. V procesorech architektury x86 se segmentace realizovala pomocí speciálních segmentových registrů (CS, DS, SS, ES, FS, GS). Každý registr ukazuje na deskriptor segmentu v globální nebo lokální deskriptorové tabulce (GDT/LDT). Deskriptor obsahuje bázovou adresu, limit a přístupová práva segmentu.

Hardwarová podpora segmentace
Obr. 5b — Hardwarová podpora segmentace na x86. Segmentový registr (např. DS pro datový segment) obsahuje selektor, který indexuje do deskriptorové tabulky (GDT nebo LDT). Deskriptor obsahuje 32bitovou bázovou adresu, 20bitový limit a příznaky (typ, přístupová práva, přítomnost v paměti). MMU automaticky při každém přístupu do paměti ověří, že offset nepřekračuje limit, a přeloží adresu. Na moderních 64bitových systémech je segmentace z velké části zrušena (segmenty mají bázi 0 a limit maximum) — nahradila ji stránkování s ochranou přístupu.
Segmentace vs. stránkování — klíčový rozdíl
Stránkování je transparentní pro programátora — stránky jsou mechanické kusy bez logického významu. Segmentace naopak odpovídá logické struktuře programu — každý segment má smysl (kód, data...). Stránkování odstraňuje fragmentaci, segmentace usnadňuje sdílení a ochranu. Ideálně se kombinují.

Segmentace se stránkováním

Nejdokonalejší přístup kombinuje výhody obou předchozích metod. Logický adresní prostor je rozdělen na segmenty (jako v čisté segmentaci), ale každý segment je dále rozdělen na stránky (jako v čistém stránkování). Fyzická paměť je rozdělena na rámce jako při stránkování — segmenty tedy nemusejí být fyzicky souvislé, čímž se eliminuje jejich fragmentace.

Logická adresa má pak tři složky: číslo segmentu, číslo stránky v rámci segmentu a offset v rámci stránky. Překlad probíhá ve dvou krocích: nejprve se segment přeloží na tabulku stránek, pak se tabulka stránek použije k nalezení fyzického rámce.

Segmentace se stránkováním — principiální schéma
Obr. 6a — Principiální schéma segmentace se stránkováním. Logická adresa se rozdělí na tři části: segment (s), stránka (p) a offset (d). Tabulka segmentů obsahuje odkaz na tabulku stránek příslušného segmentu. Ta pak obsahuje mapování na fyzický rámec. Výsledná fyzická adresa = rámec × velikost_stránky + d.
Segmentace se stránkováním — detailní překlad
Obr. 6b — Detailní překlad adresy při kombinaci segmentace a stránkování. Vidíme kompletní průběh překladu: CPU generuje trojdílnou logickou adresu. Tabulka segmentů (STBR — Segment Table Base Register) určí, kde leží tabulka stránek daného segmentu. Z tabulky stránek se získá číslo rámce. Fyzická adresa se sestaví konkatenací čísla rámce a offsetu d. Díky TLB lze většinu těchto překladů urychlit.
Segmentace se stránkováním — fyzické rozmístění
Obr. 6c — Fyzické rozmístění při kombinaci segmentace se stránkováním. Každý segment má svou vlastní tabulku stránek. Stránky téhož segmentu jsou v fyzické RAM rozmístěny libovolně — nemusejí být vedle sebe. Díky tomu není segment omezen na jednu fyzicky souvislou oblast. Různé segmenty mohou sdílet stejné fyzické rámce (sdílená paměť), aniž by to bylo vidět v logickém prostoru procesů.

Toto schéma používal například slavný procesor Intel 80386 a celá rodina IA-32. Na moderních 64bitových systémech (x86-64) je segmentace fakticky deaktivována (všechny segmenty mají bázi 0 a celý 64bitový limit) a systém pracuje prakticky jen se stránkováním — ale konceptuálně je myšlenka segmentace se stránkováním velmi elegantní a dodnes se s ní setkáváme v různých formách.

§3

Výpadek stránky, pre-cleaning a thrashing

Stránkování umožňuje fascinující trik: nemusíme mít v RAM všechny stránky procesu najednou. Stránky, které se právě nepoužívají, mohou být odloženy na disk. To dává vzniknout konceptu virtuální paměti.

Výpadek stránky (Page Fault)

Každá položka v tabulce stránek obsahuje mimo jiné příznak přítomnosti (present bit nebo valid bit). Pokud je nastaven na 1, stránka je v RAM a překlad proběhne normálně. Pokud je nastaven na 0, stránka právě není v operační paměti — je buď na disku (v odkládacím prostoru, swap), nebo ještě vůbec nebyla inicializována.

Když se procesor pokusí přistoupit ke stránce s příznakem přítomnosti = 0, nastane hardwarová výjimka zvaná výpadek stránky (page fault). Řízení přebírá obslužná rutina OS:

1. OS zjistí, zda je přístup legitimní (patří daná adresa do adresního prostoru procesu?). Pokud ne, proces dostane signál SIGSEGV a pravděpodobně selže. 2. OS najde místo na disku, kde leží data chybějící stránky. 3. OS vybere oběť — fyzický rámec, který bude uvolněn (a jehož obsah bude případně uložen na disk). 4. Data chybějící stránky se načtou z disku do uvolněného rámce. 5. Tabulka stránek se aktualizuje — příznak přítomnosti se nastaví na 1 a zaznamená se číslo nového fyzického rámce. 6. Instrukce, která způsobila výpadek, se restartuje — tentokrát už proběhne normálně.

Proč je výpadek stránky pomalý?
Disk (i SSD) je milionkrát pomalejší než RAM. Načtení stránky z disku trvá milisekundy, zatímco přístup do RAM trvá nanosekundy. Proto je klíčové, aby k výpadkům docházelo co nejméně — čehož se dosahuje chytrými algoritmy výběru obětí a dostatkem fyzické RAM.

Pre-cleaning

Když nastane výpadek stránky a OS potřebuje uvolnit rámec, musí nejprve zapsat jeho obsah na disk (pokud byl modifikován). Tato operace je časově nákladná. Pre-cleaning je technika, kdy OS preventivně zapisuje modifikované stránky na disk dříve, než jsou reálně potřeba — v době, kdy je systém méně vytížený. Tím se při samotném výpadku stránky ušetří čas čekání na zápis.

OS si vede seznam stránek, které jsou "špinavé" (dirty — byly modifikovány od posledního načtení z disku). Pozadíový démon (page daemon) tyto stránky průběžně zapisuje na disk a označuje je jako "čisté" (clean). Uvolnění čisté stránky je pak okamžité — nemusí se čekat na zápis.

Thrashing

Thrashing (česky někdy "přehazování" nebo "mlácení") je vážný stav, kdy systém tráví více času obsluhou výpadků stránek než skutečnou prací procesů. Stane se to, když je v RAM příliš mnoho procesů a každý proces dostane méně rámců, než potřebuje pro svůj pracovní set (working set — stránky, ke kterým proces pravidelně přistupuje).

V takovém stavu každý proces neustále způsobuje výpadky stránek. OS neustále načítá stránky, ale hned je musí zase vyhodit pro jiný proces. Procesory stráví 99 % času čekáním na disk. Celý systém se zdánlivě "zamrzne".

Thrashing — varovný jev
Thrashing je patologický stav: čím více procesů běží, tím víc výpadků nastane, tím pomalejší jsou všechny procesy, a CPU paradoxně nemá co dělat (jen čeká na disk), takže se OS snaží spustit další procesy... Řešením je snížit multiprogramování — ukončit nebo pozastavit některé procesy, aby zbývající dostaly dost rámců.
§4

Čisté vs. špinavé stránky

Při výběru oběti (stránky k vyhnání z RAM) je zásadní, zda stránka byla od svého načtení modifikována či nikoliv. Tato informace se sleduje pomocí jednoho bitu v tabulce stránek.

Každý záznam v tabulce stránek obsahuje tzv. dirty bit (bit modifikace, bit zápisu). Pokud proces do stránky zapsal (i jen jeden bajt), hardware automaticky nastaví dirty bit na 1. Stránka se tím stane špinavou (dirty page).

Čistá stránka (clean page)

Čistá stránka je taková, jejíž obsah v RAM je identický s obsahem na disku (ve swapu nebo v souboru). Pokud takovou stránku potřebujeme uvolnit, jednoduše ji zahodíme — data máme stále na disku, odkud je lze znovu načíst. Uvolnění čisté stránky je okamžité a bezbolestné.

Špinavá stránka (dirty page)

Špinavá stránka obsahuje data, která ještě nebyla zapsána na disk. Pokud bychom ji jednoduše zahodili, přišli bychom o změny. Proto ji musíme nejprve zapsat na disk (swap-out) a teprve pak uvolnit rámec. Tato operace trvá podstatně déle než zahození čisté stránky.

Praktický důsledek
Algoritmy výběru obětí preferují čisté stránky před špinavými, protože jejich výměna je rychlejší. Pre-cleaning (viz §3) se snaží udržovat dostatek čistých stránek tím, že špinavé stránky průběžně zapisuje na disk v době nízké zátěže.

Zajímavý případ jsou stránky obsahující spustitelný kód (text segment). Tyto stránky jsou zpravidla vždy čisté — kód se nemění. Navíc je lze uvolnit a znovu načíst přímo ze spustitelného souboru na disku, takže ani nepotřebují swap prostor. Proto na paměťově omezených systémech OS rád obětuje stránky kódu — jsou nejsnáze obnovitelné.

§5

Algoritmy výměny stránek

Algoritmus výměny stránek rozhoduje: když potřebujeme uvolnit rámec, která stránka bude obětována? Špatná volba znamená, že vyhozenou stránku okamžitě zase budeme potřebovat — a nastane další výpadek. Cílem je minimalizovat celkový počet výpadků stránek.

Pro porovnání algoritmů se používá standardní metrika: celkový počet výpadků stránek při zpracování referenčního řetězce přístupů a daném počtu dostupných rámců. Referenční řetězec je sekvence čísel stránek, ke kterým proces přistupuje v čase.

Optimální algoritmus (OPT / Bélády)

Optimální algoritmus je teoretickým maximem — vždy vyhodí tu stránku, která bude nejdéle nepoužita v budoucnosti. Je to jako mít křišťálovou kouli: víme dopředu, jaké stránky bude program žádat, a vždy se zbavíme té, která přijde na řadu nejpozději.

Výsledek je vždy nejmenší možný počet výpadků — jde o dolní mez, vůči které měříme ostatní algoritmy. Nevýhoda je fatální: v praxi je nerealizovatelný, protože OS nemá věštecké schopnosti — nezná budoucí přístupový vzor procesu. Používá se výhradně jako referenční bod pro porovnání.

Příklad
Rámce: 3, Referenční řetězec: 7,0,1,2,0,3,0,4,2,3,0,3,2. Optimální algoritmus by vždy vyhazoval stránku, na kterou se nejdéle nebude přistupovat — typicky dá o 30–40 % méně výpadků než nejlepší reálný algoritmus.

FIFO (First In, First Out)

FIFO je nejjednodušší reálný algoritmus. Princip je přímočarý: vyhodíme tu stránku, která je v paměti nejdéle — bez ohledu na to, jak často se používá. Stránky jsou uspořádány do fronty; nejstarší stránka (hlava fronty) se vyhazuje, nová stránka se přidává na konec.

Implementace je triviální — stačí cyklická fronta. Nevýhoda: stránka může být v paměti nejdéle, ale přitom být stále velmi aktivně používána (třeba inicializační rutina nebo frekventovaně volaná funkce). V takovém případě FIFO zbytečně vyhazuje užitečnou stránku.

FIFO trpí slavným Béládyho anomálií: s více rámci může nastat více výpadků stránek. To je neintuitivní — více paměti by mělo pomoci, ale FIFO to zaručit nedokáže.

LRU (Least Recently Used)

LRU vychází z principu lokality: co bylo nedávno použito, bude pravděpodobně použito znovu brzy. Naopak co nebylo použito dlouho, pravděpodobně nebude potřeba ani v blízké budoucnosti. LRU proto vyhazuje stránku, která nebyla nejdelší dobu použita.

LRU je přibližnou implementací optimálního algoritmu s pohledem do minulosti místo do budoucnosti. V praxi funguje velmi dobře — minulé chování je dobrým prediktorem budoucnosti (princip lokality přístupů). Bohužel přesná implementace LRU je nákladná: při každém přístupu do paměti musíme zaznamenat čas přístupu a při výběru oběti projít všechny rámce a najít nejstarší. To je prakticky nereálné v hardware.

Proto se používají aproximace LRU — nejčastěji pomocí reference bitu (use bitu): hardware nastaví tento bit na 1, kdykoli se stránka použije. OS perioidicky bity nuluje a sleduje, které stránky mezi nulovacími intervaly nebyly použity (bit zůstal 0) — ty jsou kandidáty na vyhazení. Tento přístup je základem algoritmů Druhá šance a Hodiny.

Druhá šance (Clock s bitem R)

Algoritmus Druhá šance je FIFO vylepšené o referenční bit (R bit). Místo slepého vyhazování nejstarší stránky dáme každé stránce "druhou šanci": pokud má R = 1 (byla nedávno použita), nulujeme R a přesuneme ji na konec fronty — jako by právě přišla do paměti. Pokud má R = 0, vyhodíme ji.

Výsledkem je, že aktivně používané stránky (R = 1) dostávají stále nové šance a zůstávají v paměti, zatímco nepoužívané stránky (R = 0) jsou odstraněny i přesto, že jsou třeba "staré" jen o málo.

Algoritmus Hodiny (Clock)

Hodiny jsou efektivní implementací algoritmu Druhá šance. Místo fronty se používá cyklický seznam (jako ciferník hodin) a jeden ukazatel ("ručička"). Ručička postupuje po ciferníku:

Pokud stránka, na kterou ukazuje ručička, má R = 1, nastavíme R = 0 a posuneme ručičku dál. Pokud má R = 0, tuto stránku vyhodíme a na její místo dáme novou stránku, pak ručičku posuneme. V nejhorším případě ručička oběhne celý kruh a vyhodí stránku, která má R = 0 po jednom průchodu (= nepoužila se od posledního průchodu ručičky).

Algoritmus Hodiny je velmi praktický: je efektivní, jednoduchá implementace, a dobře aproximuje LRU. Používá se (nebo se používaly varianty) v mnoha reálných OS, včetně starších verzí Linuxu a BSD.

NUR / NRU (Not Recently Used / Not Used Recently)

NUR (nebo NRU — záleží na zdroji, jde o tentýž koncept) rozšiřuje sledování o dva bity: referenční bit R (byl přistoupen) a modifikační bit M (byl modifikován, dirty bit). Kombinací těchto bitů vznikají čtyři třídy stránek:

TřídaRMPriorita vyhazeníInterpretace
000NejvyššíNebyla přistoupena ani modifikována — ideální oběť
101DruháModifikována, ale nedávno nepřistoupena — musí se zapsat na disk
210TřetíPřistoupena, ale čistá — stále aktivní, ale uvolnitelná bez zápisu
311NejnižšíPřistoupena i modifikována — aktivně používaná, vyhodit jako poslední

OS vybere jako oběť stránku z nejnižší neprázdné třídy. Bity R se periodicky nulují (každý časový interval), aby se zachytil aktuální vzor přístupů. Algoritmus je jednoduchý, nenákladný a dobře aproximuje LRU s bonusem rozlišení čistých a špinavých stránek.

Náhodný výběr (Random)

Algoritmus Random je přesně tak prostý, jak název napovídá: oběť se vybere náhodně z dostupných rámců. Nesleduje žádné přístupové vzory, nepoužívá žádné bity.

Výhodou je absolutní jednoduchost a nulová režie. Překvapivě není špatný ve srovnání s FIFO, protože alespoň netrpí Béládyho anomálií a v průměrném případě nevybírá systematicky špatně. Hlavní nevýhoda je nepředvídatelnost — v nejhorším případě vždy vyhazuje stránky, které budou okamžitě potřeba. Používá se v systémech s velmi omezenými prostředky nebo jako záložní mechanismus.

NFU (Not Frequently Used)

NFU sleduje celkový počet použití každé stránky od chvíle, kdy přišla do paměti. Každý časový interval OS projde všechny stránky, přičte referenční bit R k čítači dané stránky (a R vynuluje). Vyhazuje se stránka s nejmenším čítačem.

Myšlenka je, že stránka s malým čítačem se za svůj pobyt v paměti moc nepoužívala. Problém: NFU zapomínat neumí. Stránka, která se v minulosti hodně používala, ale nyní je nečinná, má stále vysoký čítač a nebude vyhazována — i když ji program nepotřebuje. Naopak nová stránka, která je nyní aktivně využívána, má čítač blízký nule a může být předčasně vyhozena.

Řešením je varianta NFU se stárnutím (aging): před přičtením R bitu se čítač posune doprava o jeden bit (dělení dvěma). Starší přístupy tak mají stále menší vliv — čítač "stárne". Tato varianta výborně aproximuje LRU a je efektivně implementovatelná.

NFU se stárnutím: čítač[stránka] = (čítač[stránka] >> 1) | (R_bit << (n-1)) kde n = počet bitů čítače, R_bit = 0 nebo 1

Operace posunutí doprava (>>) dělí čítač dvěma — starší přístupy mají exponenciálně klesající vliv. Přičtení R_bitu na nejvyšší pozici zaznamenává aktuální referenci. Výsledkem je "klouzavý průměr" s exponenciálním zapomínáním.


Souhrnné srovnání algoritmů výměny stránek

Algoritmus Princip výběru oběti Implementační náročnost Kvalita Béládyho anomálie
OPT Nejdéle nepoužitá v budoucnosti Nerealizovatelný Optimální (dolní mez) Ne
FIFO Nejdéle v paměti Velmi nízká (fronta) Špatná Ano ⚠️
LRU Nejdéle nepoužitá v minulosti Vysoká (přesná impl.) Velmi dobrá Ne
Druhá šance FIFO + R bit Nízká Dobrá Ne
Hodiny Cyklické FIFO + R bit Nízká (cyklický buf.) Dobrá Ne
NUR/NRU R=0, M=0 → nejlepší oběť Nízká (2 bity/stránku) Dobrá Ne
Random Náhodná stránka Minimální Průměrná Ne
NFU / Aging Nejmenší čítač použití Střední Dobrá (aging verze) Ne
Co používají reálné OS?
Linux používá sofistikovanou variantu algoritmu Hodiny nazvanou CLOCK-Pro, doplněnou o rozlišení aktivních a neaktivních stránek (active/inactive list). Windows NT a novější používají variantu LRU aproximovanou přes working sets a reference bity. Cílem je minimalizovat výpadky stránek při současném co nejnižším overhead překladu adres.
🏠