🖥️ BI-OSY – Operační systémy

Státnicové poznámky · Bakalářské státní zkoušky · FIT ČVUT

BI-SPOL.21-17 BI-SPOL.21-18 Červen 2025 Přednášky 2, 3, 5, 7, 8, 9
Okruh 1 · BI-SPOL.21-17

Procesy, vlákna, synchronizace a deadlock

Proces vs. vlákno, implementace, přepínání kontextu, stavy vláken, synchronizační nástroje, klasické synchronizační úlohy, uváznutí a strategie jeho řešení.

📦 Program, Proces, Vlákno – základní pojmy

Program je spustitelný binární soubor uložený na disku. Formát závisí na OS: ELF (Linux/Unix), PE/PE32+ (Windows). Obsahuje sekce .text (kód) a .data (data).

Proces = instance spuštěného programu. Entita, v rámci které jsou alokovány prostředky (paměť, vlákna, otevřené soubory, zámky, semafory, sokety...). Každý proces implicitně obsahuje jedno „main" vlákno.
Vlákno = výpočetní entita (proud instrukcí), které je přidělováno jádro CPU. Historicky se nazývalo „light-weight process" (lwp). Vlákna sdílejí prostředky procesu, ale každé má vlastní zásobník a kontext.

Co si OS udržuje pro každý proces (PCB – Process Control Block):

  • Identifikace: PID (Process ID), PPID (Parent PID), SID (Session ID)
  • Bezpečnost: identita vlastníka (EUID, RUID), skupiny (GID), přístupová práva
  • Správa paměti: informace pro překlad virtuálních adres (page table – struct mm_struct)
  • Správa souborů: tabulka deskriptorů souborů
  • IPC prostředky: semafory, roury, signály

Co si OS udržuje pro každé vlákno (TCB – Thread Control Block):

  • TID (Thread ID)
  • Hodnoty registrů CPU, čítač instrukcí (PC) – nutné pro přepínání kontextu
  • Zásobník (stack) – lokální proměnné a history volání
  • Priorita, stav vlákna, čas strávený na CPU
Klíčový rozdíl

Procesy mají oddělené adresní prostory (paměť). Vlákna v rámci jednoho procesu sdílejí adresní prostor – to umožňuje efektivní komunikaci, ale přináší riziko race conditions.

🔄 Vytvoření a ukončení procesu

Nový proces vznikne, když existující proces zavolá příslušné systémové volání:

  • Unix/Linux: fork(2), execve(2)
  • Windows: CreateProcessA()

fork() – vytvoří kopii (klon) aktuálního procesu:

  • Vrací číslo potomka v rodiči, v potomkovi vrací 0, při chybě -1
  • Jádro alokuje nové datové struktury; adresový prostor se „zkopíruje" od rodiče (v praxi pomocí Copy-on-Write)
  • Čítač instrukcí ukazuje na instrukci za fork() – rodič i potomek pokračují dál na sobě nezávisle

execve(filename, argv, envp) – nahradí adresový prostor novým programem:

  • Adresový prostor se přepíše obsahem spustitelného souboru
  • Nový PC ukazuje na první instrukci nového programu

wait(int *status) – rodič zablokuje, dokud potomek neskončí; potomkova exit hodnota se uloží do status.

Životní cyklus procesu při ukončení (Linux)
  • Zavolání exit() nebo návrat z main → systémové volání exit
  • Nastaví se exit_state = zombie, zápis návratového kódu do exit_code
  • Rodič se notifikuje signálem → přečte návratový kód pomocí wait() → struktura task_struct potomka se uvolní
  • Pokud rodič skončí dříve → OS přenastaví ukazatel rodiče na init (init adoptuje sirotky)

⚙️ Plánování vláken a přepínání kontextu

Jádro OS a vlákna sdílejí omezený počet jader CPU. Jedno vlákno může být v jednom okamžiku zpracováváno jedním logickým jádrem. Vlákna se na jádrech střídají pomocí preemptivního plánování.

Preemptivní plánování:

  • Vláknu je přiděleno jádro CPU na základě plánovacích kritérií (priorita, ...)
  • Přidělí se časové kvantum
  • CPU je vláknu odebráno, pokud: uplyne časové kvantum (přerušení od časovače), vlákno provede systémové volání (např. V/V), nastane přerušení
Přepínání kontextu = mechanismus, při kterém se vlákna vystřídají v používání jádra CPU. Kontext = všechny informace nutné pro pozdější spuštění vlákna od okamžiku přerušení (čítač instrukcí, hodnoty registrů...).

Postup přepnutí kontextu:

  1. Uloží se kontext původního vlákna (registry, PC, zásobníkový ukazatel)
  2. Jádro OS naplánuje další vlákno
  3. Nastaví se kontext nového vlákna

Stavy vlákna:

StavPopis
IdleVznik nového vlákna
ReadyVlákno čeká, až mu bude přiděleno jádro CPU
RunningVlákno je zpracováváno jádrem CPU
BlockedVlákno čeká na událost (V/V, signál, mutex...)
ZombieVlákno se ukončuje, ale ještě ne vše dokončeno
FreeVlákno bylo kompletně zrušeno (teoretický stav)
Implementace vláken – POSIX

API pro vlákna: pthread_create(&tid, NULL, &start_routine, NULL) – vytvoří vlákno vykonávající start_routine. pthread_join(tid, &result) – čeká na dokončení vlákna.

⚠️ Časově závislé chyby (Race Conditions) a kritické sekce

Časově závislé chyby (race conditions) = situace, kdy dvě nebo více vláken čte/zapisuje sdílené prostředky a výsledek deterministického algoritmu závisí na rychlosti jednotlivých vláken. Výskyt je náhodný → špatně se detekují!

Klasický příklad: Dvě vlákna inkrementují sdílený čítač.

// Vlákno 1:          // Vlákno 2:
LOAD  R, [A]         LOAD  R, [A]
INC   R              INC   R
STORE [A], R         STORE [A], R
// Pokud se proloží → výsledek je 1 místo 2!
Kritická sekce = část programu, kde vlákna přistupují ke sdíleným prostředkům. Sdružené kritické sekce = kritické sekce dvou a více vláken, které se týkají stejného prostředku.
Vzájemné vyloučení (Mutual Exclusion) = vlákna nesmí vstupovat do sdružených kritických sekcí současně.

Pravidla korektního paralelního programu:

  1. Žádné předpoklady nesmí být kladeny na rychlost vláken a počet jader
  2. Zajistit výlučný přístup ke sdíleným prostředkům (žádné vlákno nesmí čekat nekonečně dlouho)
  3. Mimo kritickou sekci nesmí být vlákno blokováno ostatními vlákny

🔒 Synchronizační nástroje

Základní přístupy k synchronizaci kritické sekce:

1. Aktivní čekání (Busy Waiting / Spinning)

Sdílená proměnná indikuje obsazenost sekce. Vlákno čekající na vstup opakovaně testuje stav proměnné ve smyčce. Implementace vyžaduje atomickou instrukci:

TSL (Test-and-Set-Lock) = atomická instrukce: načte hodnotu z paměti do registru A současně nastaví hodnotu v paměti na nenulovou (zamkne sekci). Reálné varianty: xchg (x86), cas (SPARC), amoswap (RISC-V).
  • ✅ Výhoda: minimální režie při krátkém čekání
  • ❌ Nevýhoda: čekání zatíží CPU na 100%; může způsobit inverzní prioritní problém
Inverzní prioritní problém

Nastane, pokud: vlákno A (nízká priorita) je v kritické sekci; vlákno B (vyšší priorita) čeká pomocí busy waiting; všechna jádra obsazena vlákny s vyšší prioritou → vlákno A nikdy nedostane CPU → uváznutí!

2. Blokující volání (Mutex / Zámek)

Mutex (MUTual EXclusion lock) = synchronizační prostředek pamatující si stav (zamčený/odemčený) a frontu čekajících vláken. Operace: mutex_lock() – zamkne nebo zablokuje vlákno; mutex_unlock() – odemkne nebo probudí čekající vlákno.

POSIX: pthread_mutex_t, funkce pthread_mutex_lock(), pthread_mutex_unlock(). C++: std::mutex, std::lock_guard.

  • ✅ Výhoda: čekání nezatěžuje CPU
  • ❌ Nevýhoda: větší režie (změna stavu vlákna v jádře OS) → vyplatí se při delším čekání

3. Podmíněné proměnné (Condition Variables)

Podmíněná proměnná = pamatuje si, která vlákna jsou na ní blokována. Operace cond_wait(cond, mutex) – atomicky uvolní mutex a zablokuje vlákno (musí být voláno s uzamčeným mutexem; po probuzení mutex opět zamkne). cond_signal(cond) – probudí aspoň jedno blokované vlákno.
Proč while místo if u cond_wait?

Spurious wakeup (falešné probuzení) – POSIX umožňuje probuzení vlákna bez zavolání cond_signal(). Také při více čekajících vláknech může podmínka přestat platit. Proto vždy: while(podminka) cond_wait(...);

4. Semafor

Semafor = celočíselný čítač + fronta čekajících vláken. Operace: sem_wait() – sníží čítač o 1 (pokud 0, zablokuje vlákno); sem_post() – zvýší čítač o 1 (nebo probudí čekající vlákno). Binární semafor (hodnoty 0/1) ≈ mutex.

POSIX: sem_t, sem_init(), sem_wait(), sem_post().

5. Bariéra

Bariéra = synchronizační nástroj pro iterační výpočty. Obsahuje čítač (síla bariéry) a frontu čekajících vláken. barrier_wait() – zablokuje vlákno, dokud se na bariéře nesejde přesný počet vláken; pak jsou všechna najednou uvolněna a čítač se resetuje.

POSIX: pthread_barrier_t, pthread_barrier_wait(). Použití: maticové výpočty, paralelní simulace.

🏭 Klasické synchronizační úlohy

Producent–Konzument (Bounded Buffer Problem)

Producent vkládá data do sdílené fronty s omezenou velikostí N. Konzument data vybírá. Musíme řešit:

  1. Výlučný přístup při vkládání/vybírání
  2. Fronta prázdná → zablokuj konzumenta
  3. Fronta plná → zablokuj producenta

Řešení pomocí semaforů:

sem_t empty;  // sem_init(&empty, N);  // počítá prázdná místa
sem_t full;   // sem_init(&full, 0);   // počítá plná místa
mutex_t mtx;  // mutex pro ochranu fronty

// Producent:          // Konzument:
sem_wait(&empty);      sem_wait(&full);
mutex_lock(&mtx);      mutex_lock(&mtx);
vlo_item();            vyber_item();
mutex_unlock(&mtx);    mutex_unlock(&mtx);
sem_post(&full);       sem_post(&empty);

Řešení pomocí podmíněných proměnných:

// Producent:
mutex_lock(&mtx);
while(count == N) cond_wait(&cv_full, &mtx);
insert_item();
count++;
cond_signal(&cv_empty);
mutex_unlock(&mtx);
Přehled synchronizačních nástrojů
NástrojTyp problémuPOSIX API
MutexKritická sekcepthread_mutex_t
Podmíněná proměnnáČekání na podmínkupthread_cond_t
SemaforPočítání zdrojů, KSsem_t
BariéraSynchronizace iteracípthread_barrier_t
SpinlockKrátká KS, vícejadrové sys.HW instrukce (TSL)

💀 Uváznutí (Deadlock) – definice a příčiny

Uváznutí (deadlock) = situace, kdy několik vláken čeká na událost/prostředek, kterou může vyvolat/uvolnit pouze jedno z čekajících vláken. Oproti tomu: Livelock = vlákna vykonávají neužitečný výpočet, ale nemohou pokročit. Hladovění (starvation) = vlákno je opakovaně předbíháno a nedostane se k prostředkům.

Výpočetní prostředky mohou být:

  • Fyzické: fyzická paměť, tiskárna, ...
  • Logické: proměnná, soubor, mutex, semafor, ...
  • Odnímatelné (preemptable): lze odebrat bez rizika (např. CPU)
  • Neodnímatelné (nonpreemptable): nelze bezpečně odebrat (většina prostředků)

Alokační graf znázorňuje alokaci prostředků vlákny (orientovaný graf). Hrana od prostředku k vláknu = alokovaný prostředek. Hrana od vlákna k prostředku = vlákno čeká. Každá smyčka v grafu = uváznutí.

📋 Coffmanovy podmínky

Uváznutí nastane pouze pokud jsou splněny všechny čtyři podmínky současně:

#PodmínkaVysvětlení
1 Vzájemné vyloučení Každý prostředek je přidělen právě jednomu vláknu nebo je volný (nelze sdílet)
2 Neodnímatelnost Přidělený prostředek nemůže být vláknu násilím odebrán – musí být dobrovolně uvolněn
3 Drž a čekej Vlákno, které má přiděleny prostředky, může žádat o další (postupná alokace)
4 Kruhové čekání Existuje smyčka vláken, kde každé čeká na prostředek přidělený dalšímu vláknu ve smyčce
Klíčové

Podmínky 1–3 jsou nutné, ale ne dostačující pro vznik uváznutí. Podmínka 4 (kruhové čekání) je samotné uváznutí. Pokud je alespoň jedna podmínka porušena, uváznutí nemůže nastat.

🛡️ Strategie pro řešení uváznutí

1. Pštrosí strategie (ignorování problému)

  • Problém se neřeší – pokud uváznutí nastane, vyžaduje se zásah uživatele/administrátora
  • Vhodná, pokud: systém obsahuje velký počet různě se chovajících vláken, pravděpodobnost uváznutí je malá, řešení by bylo příliš drahé
  • Praktické řešení většiny univerzálních OS (Linux, Windows) – jádro je navrženo jako „deadlock free", na úrovni procesů se řeší částečně (limity ulimit)

2. Prevence uváznutí (porušení jedné z Coffmanových podmínek)

  • Porušení vzájemného vyloučení: v praxi téměř nerealizovatelné bez race conditions
  • Porušení neodnímatelnosti: vhodné, kde lze uložit stav (např. přepínání kontextu CPU)
  • Porušení „drž a čekej": alokovat všechny prostředky najednou před použitím (worse utilization)
  • Porušení kruhového čekání: číslování prostředků + alokace jen ve vzestupném pořadí → smyčka nemůže vzniknout. Příklad: večeřící filozofové – filosof musí brát vidličky ve vzrůstajícím pořadí čísel

3. Předcházení vzniku uváznutí (Banker's Algorithm)

Předpoklady: Předem známé požadavky vláken na prostředky. Koncept bezpečného stavu:

Bezpečný stav = existuje taková posloupnost přidělování prostředků, která garantuje postupné uspokojení potřeb všech vláken.

Používané matice/vektory: E (existence vector), F (free vector), Q (request matrix – celkové požadavky), A (allocation matrix – aktuálně alokované), M = Q - A (missing matrix).

Algoritmus detekce bezpečného stavu:

  1. Existuje vlákno, které lze uspokojit volnými prostředky? → označ je, přidej jeho prostředky k volným, opakuj
  2. Byla označena všechna vlákna? → ANO = bezpečný stav, NE = nebezpečný stav
Nevýhoda

Bankéřův algoritmus vyžaduje znát předem všechny požadavky vláken a je výpočetně náročný. V praxi se pro obecné OS nepoužívá.

4. Detekce a zotavení

Uváznutí se detekuje periodickými kontrolami (algoritmus podobný bankéřovu, ale pro aktuální požadavky Qc). Zotavení:

  • Ukončení všech uvázlých vláken – typické v OS
  • Postupné ukončování – dokud uváznutí přetrvává
  • Rollback a restart – vyžaduje checkpoint mechanismus; pravděpodobnost opakovaného uváznutí je nízká díky nedeterminismu
Shrnutí strategií
StrategiePrincipPoužití v praxi
PštrosíIgnoruj problémVětšina univerzálních OS
PrevencePorušení Coffmanovy podmínkyNávrh protokolů, RT systémy
PředcházeníBankéřův algoritmusSpeciální systémy s předvídatelnými požadavky
Detekce + zotaveníDetekce smyček, oběťDB systémy, transakční systémy

📌 Shrnutí okruhu 1

  • Proces = entita s prostředky (pamětí, soubory...); vlákno = entita s CPU časem. Vlákna jednoho procesu sdílejí paměť.
  • Přepínání kontextu = uložení + obnovení stavu (registrů, PC) vlákna. Probíhá transparentně – vlákno „nevnímá" přerušení.
  • Race conditions vznikají nechráněným přístupem ke sdíleným datům. Mutex, semafor a podmíněné proměnné zajišťují vzájemné vyloučení.
  • Deadlock nastane, pokud jsou splněny všechny 4 Coffmanovy podmínky najednou. Nejběžnější přístup: pštrosí strategie + omezování prostředků.
  • Bankéřův algoritmus umožňuje předcházet uváznutí za cenu znalosti požadavků vláken dopředu a výpočetní režie.

Kontrolní otázky ke státnicím

1. Jaký je rozdíl mezi procesem a vláknem?

Proces je entita vlastnící prostředky (paměť, soubory, zámky). Vlákno je výpočetní entita v rámci procesu, které je přidělováno CPU. Vlákna jednoho procesu sdílejí VAS a prostředky procesu, ale každé má vlastní zásobník a kontext (registry, PC). Přepínání kontextu mezi vlákny téhož procesu je rychlejší, protože nevyžaduje změnu adresního prostoru.

2. Co jsou Coffmanovy podmínky a proč jsou důležité?

Jsou to 4 nutné podmínky pro vznik deadlocku: (1) vzájemné vyloučení, (2) neodnímatelnost prostředků, (3) „drž a čekej", (4) kruhové čekání. Jsou důležité, protože k uváznutí může dojít pouze pokud jsou splněny všechny čtyři najednou. Porušením libovolné jedné podmínky zabráníme uváznutí.

3. Co je race condition a jak se jí předchází?

Race condition nastane, když výsledek programu závisí na pořadí (načasování) přístupu vláken ke sdíleným datům. Předchází se ochranou kritických sekcí synchronizačními nástroji: mutex zajistí, že do kritické sekce vstupuje vždy jen jedno vlákno. Podmíněné proměnné a semafory koordinují přístup k prostředkům s podmínkami (plná/prázdná fronta).

4. Co je TSL instrukce a proč ji potřebujeme?

TSL (Test-and-Set-Lock) je atomická hardwarová instrukce, která najednou přečte hodnotu z paměti do registru a nastaví slovo v paměti na nenulovou hodnotu. Atomičnost zaručuje, že žádné jiné vlákno nemůže přistoupit ke slovu během provádění instrukce. Bez ní by samotná sdílená proměnná nestačila (kontrola a nastavení by nebyly atomické → race condition).

5. Jak funguje přepínání kontextu a kdy k němu dochází?

Přepínání kontextu = uložení stavu aktuálního vlákna (registry, PC, SP) do TCB, výběr nového vlákna plánovačem, načtení stavu nového vlákna. Dochází k němu při: (1) vypršení časového kvanta (přerušení od časovače), (2) systémovém volání (vlákno se může zablokovat), (3) přerušení od V/V zařízení, (4) dobrovolném vzdání se CPU.

6. Vysvětlete Bankéřův algoritmus.

Bankéřův algoritmus brání uváznutí tím, že prostředek přidělí, jen pokud systém zůstane v bezpečném stavu. Bezpečný stav = existuje pořadí uspokojení všech vláken. Algoritmus: hledá vlákno, jehož zbývající požadavky (M = Q - A) lze uspokojit volnými prostředky (F). Takovéto vlákno „uspokojíme" a jeho prostředky přičteme k volným. Opakujeme. Pokud uspokojíme všechna vlákna → stav bezpečný. Vyžaduje znát předem všechny požadavky.


Okruh 2 · BI-SPOL.21-18

Virtualizace hlavní paměti stránkováním

Virtuální adresní prostor (VAS), princip stránkování, překlad virtuálních adres na fyzické, tabulky stránek (jednoúrovňová, víceúrovňová, invertovaná), TLB, algoritmy pro nahrazování stránek.

🗺️ Virtuální adresní prostor (VAS) – motivace a princip

Virtuální adresní prostor (VAS) = abstrakce, která každému procesu vytváří jeho vlastní soukromý adresní prostor. Procesy pracují s virtuálními adresami, které se automaticky překládají na fyzické.

Proč VAS?

  • Ochrana paměti: nelze číst/přepisovat data jiného procesu → bezpečnost (hesla, PIN), stabilita (pád procesu neovlivní ostatní)
  • Jednodušší zavádění programu: lze zavést do libovolné volné fyzické paměti bez přepočítávání adres
  • Větší adresní prostor než fyzická paměť: 32-bit OS → každý proces může adresovat 4 GB; fyzicky může být méně

Segmenty VAS: .text (kód), .data (globální proměnné), heap (halda, roste nahoru), libraries (sdílené knihovny), stack (zásobník, roste dolů).

Nevýhody VAS

Překlad adres vyžaduje podporu v HW (MMU) a OS → složitost. Překlad adres chvíli trvá → zpomalení (řeší TLB).

📄 Stránkování – základní princip

Stránkování (Paging) = mechanismus správy paměti, kde VAS i fyzická paměť jsou rozděleny na stejně velké bloky. Bloky VAS = stránky (pages). Bloky fyzické paměti = rámce (frames). Typická velikost: 4 KB (Intel x86), 8 KB (SPARC).

Proč stránkování?

  • Elegantně řeší fragmentaci fyzické paměti – každé stránce lze přiřadit libovolný rámec
  • Celý VAS nemusí být v paměti – stačí pouze aktuálně používané stránky
  • Nepoužívané stránky mohou být odloženy na disk (swap)

Struktura virtuální adresy:

Dělení virtuální adresy

[číslo stránky (page number) | offset (byte uvnitř stránky)]

Příklad: 32-bit CPU, 4 KB stránky → offset = 12 bitů, číslo stránky = 20 bitů → 2²⁰ = 1M stránek.

Výpadek stránky (Page Fault) = situace, kdy proces přistoupí k virtuální adrese, jejíž stránka není v hlavní paměti. MMU způsobí přerušení, OS obslouží výjimku: najde volný rámec (nebo odloží stránku na disk), nahraje požadovanou stránku, aktualizuje tabulku stránek, restartuje instrukci.

🔧 MMU a jednoúrovňová stránkovací tabulka

MMU (Memory Management Unit) = hardwarová jednotka, která provádí překlad virtuálních adres na fyzické za spolupráce s OS pomocí stránkovacích tabulek. Registr PTBR (Page Table Base Register) ukazuje na aktuální stránkovací tabulku (při přepnutí procesu se aktualizuje).

Jednoúrovňová stránkovací tabulka obsahuje pro každou stránku VAS jeden řádek:

  • Číslo rámce – kam je stránka namapována
  • Řídicí bity:
    • P (Present) – stránka je v hlavní paměti
    • A (Accessed) – ke stránce se přistupovalo
    • D (Dirty) – obsah byl modifikován (před odložením nutno zapsat na disk)
    • W (Write) – lze stránku modifikovat?
    • X (Execute) – stránka obsahuje spustitelný kód?
    • U (User/Supervisor) – přístupná v uživatelském modu?
    • G (Global) – platná ve všech procesech (sdílené jádro OS)
    • C (Cache) – lze používat cache? (zakázat pro registry periferií)

Překlad adresy (jednoúrovňová ST):

  1. Rozděl virtuální adresu na číslo stránky (index do ST) a offset
  2. Přečti řádek ST[číslo stránky] → zkontroluj bit P
  3. Bit P = 1: fyzická adresa = číslo rámce + offset → přístup do paměti
  4. Bit P = 0: výpadek stránky → OS obsluha
Problém jednoúrovňové ST

Pro 32-bit CPU s 4 KB stránkami: ST má 2²⁰ = 1M řádků × 4 B = 4 MB na jeden proces. Pro 128 procesů = 512 MB jen pro ST! Přitom většina ST je prázdná (VAS využit z malé části).

Řešení: víceúrovňová stránkovací tabulka.

🌲 Víceúrovňová stránkovací tabulka

Víceúrovňová ST = hierarchická struktura tabulek, kde tabulky nižší úrovně existují v paměti jen pro části VAS, které proces skutečně používá. Virtuální adresa se skládá z n indexů (po jednom pro každou úroveň) + offset.

Princip (n-úrovňová ST):

  • V paměti je vždy pouze top-level tabulka (úroveň 1)
  • Tabulky úrovní 2 ... n existují jen pro používané části VAS → úspora paměti
  • Každá tabulka úrovně 1 ... n-1 obsahuje Present bit + číslo rámce s tabulkou další úrovně
  • Tabulka úrovně n obsahuje Present bit + číslo rámce s daty

Příklad – dvouúrovňová ST (x86 32-bit):

Virtuální adresa: [10 bitů L1 index | 10 bitů L2 index | 12 bitů offset]

Pro 128 procesů stejné zátěže jako dříve: 128 × [1 × (1024 × 4B) + 3 × (1024 × 4B)] = 128 × 16 KB = 2 MB (oproti 512 MB u jednoúrovňové!).

Reálné architektury:

ArchitekturaÚrovně STVirtuální adresa
x86 (32-bit)2 úrovně32 bit: 10+10+12
x86-64 (64-bit)4 úrovně (CR3)48 bit: 9+9+9+9+12
SPARC v9Invertovaná ST (TSB)
Nevýhoda víceúrovňové ST

Překlad adresy vyžaduje více přístupů do paměti (pro n úrovní = n+1 přístupů). Řeší TLB – cache přeložených adres.

🔀 Invertovaná stránkovací tabulka

Invertovaná ST = hašovací tabulka, kde je pro každý rámec fyzické paměti jeden řádek (ne pro každou stránku). Jeden řádek obsahuje: číslo stránky, číslo procesu (PID), řídicí bity, index zřetězení (chain). OS udržuje jednu tabulku pro celý systém.

Překlad adresy:

  1. Hašovací funkce z (PID, číslo stránky) → index do invertované ST
  2. Porovnej záznam v tabulce s (PID, číslo stránky)
  3. Shoda → číslo řádku = číslo rámce → fyzická adresa = rámec + offset
  4. Neshoda → procházej zřetězení (chain) → nebo výpadek stránky
Typ STVelikostRychlost překladuPoužití
JednoúrovňováVelká (na proces)Rychlá (1 přístup)Jednoduché systémy
VíceúrovňováMalá (pro sparse VAS)Pomalejší (n přístupů)x86, ARM, RISC-V
InvertovanáMalá (jedna pro systém)Pomalá (hash + chain)PowerPC, UltraSPARC

Translation Lookaside Buffer (TLB)

TLB (Translation Lookaside Buffer) = hardware cache překladů virtuálních adres, implementovaná jako n-cestná asociativní paměť. Obsahuje nedávno přeložené dvojice (číslo stránky → číslo rámce).

Obsah jedné položky TLB:

  • valid bit – platná položka?
  • Číslo stránky (page number)
  • Číslo rámce (frame number)
  • ASID – Address Space ID (identifikátor adresního prostoru – aby TLB mohl být sdílený více procesy)
  • Kopie řídicích bitů ze stránkovací tabulky (W, X, U...)

Průběh překladu s TLB:

  1. MMU hledá číslo stránky v TLB → TLB Hit: fyzická adresa = rámec z TLB + offset → velmi rychlé (~1 cyklus)
  2. TLB Miss: MMU prohledá stránkovací tabulky v paměti, aktualizuje TLB, přeloží adresu
  3. Při výpadku stránky: OS obslouží, aktualizuje ST, TLB, restartuje instrukci
Efektivita TLB

Díky principu lokality je TLB hit ratio obvykle >99%. Průměrný čas přístupu: t_avg = hit_ratio × t_TLB + (1 - hit_ratio) × t_pamet. Příklad: hit_ratio = 0.99, t_TLB = 1 ns, t_pamet = 50 ns → t_avg ≈ 1.5 ns.

TLB a přepnutí procesu

Při přepnutí procesu musí OS zneplatnit položky TLB patřící předchozímu procesu (flush TLB) nebo použít ASID k rozlišení procesů. Flush TLB je drahá operace – ASID umožňuje zachovat platné záznamy i po přepnutí kontextu.

🔁 Obsluha výpadku stránky (Page Fault Handler)

OS obsluha výpadku stránky (krok za krokem):

  1. Ověření platnosti virtuální adresy (leží ve VAS?) a oprávněnosti přístupu (zápis do R/O stránky?) → pokud ne: Segmentation Fault
  2. Nalezení volného rámce (Get Free Frame):
    • Pokud existuje volný rámec → použij jej
    • Pokud ne → vyber oběť pomocí algoritmu pro náhradu stránek (PRA)
    • Je oběť dirty (D bit)? → zapiš ji nejprve na disk (swap)
    • Zneplatni záznamy ST a TLB pro odložený rámec ve všech dotčených procesech
  3. Načtení stránky do rámce:
    • Ze souboru (file-backed VMA, např. .text, .data programu, mmap)
    • Ze swapu (anonymní stránka dříve odložená)
    • Inicializace nulou (nová anonymní stránka – heap, stack)
  4. Aktualizace stránkovacích tabulek (nastavení P bitu, čísla rámce)
  5. Aktualizace TLB
  6. Restart přerušené instrukce
Linux: Virtual Memory Areas (VMA)

Linux spravuje VAS pomocí struktur vm_area_struct. Každá VMA je spojitý blok virtuální paměti s přístupovými právy. Může být file-backed (propojená se souborem, např. spustitelný soubor, knihovna) nebo anonymní (halda, zásobník, .bss). Platná virtuální adresa musí ležet v některé VMA.

🧮 Algoritmy pro náhradu stránek (Page Replacement Algorithms)

Cíl: minimalizovat počet výpadků stránek. Vychází z principu časové a prostorové lokality.

1. Optimální algoritmus (OPT)

  • Nahradí stránku, ke které se bude přistupovat nejpozději v budoucnosti
  • ✅ Generuje minimální počet výpadků (dolní mez)
  • ❌ Nerealizovatelný (neznáme budoucnost) – slouží jako benchmark

2. NRU algoritmus (Not Recently Used)

  • Třídy stránek podle R (referenced) a D (dirty) bitů: Třída 0 (R=0,D=0), 1 (R=0,D=1), 2 (R=1,D=0), 3 (R=1,D=1)
  • Nahradí stránku z neprázdné třídy s nejnižším číslem
  • OS periodicky resetuje R bit
  • ✅ Jednoduchý, rozumný počet výpadků

3. FIFO algoritmus (First-In First-Out)

  • Nahradí stránku, která je v paměti nejdéle (hlava fronty)
  • ✅ Jednoduchý na implementaci
  • ❌ Nezohledňuje frekvenci přístupu → velký počet výpadků; trpí Bélády anomálií (více rámců → více výpadků)

4. Clock algoritmus (modifikovaný FIFO)

  • Kruhová fronta + ručička (ukazatel). Každá stránka má R bit.
  • Při hledání oběti: pokud R=1 → vynuluj R, posuň ručičku. Pokud R=0 → tato stránka je oběť.
  • ✅ Lepší než FIFO, rozumná implementace. Varianta se dvěma ručičkami (two-handed clock) – používá se ve SVR4 Unix a nástupcích.

5. LRU algoritmus (Least Recently Used)

  • Nahradí stránku, ke které se přistupovalo nejdéle (nejstarší přístup)
  • Dobrá aproximace optimálního algoritmu (využívá časovou lokalitu)
  • ✅ Nízký počet výpadků
  • ❌ Implementace: nutný speciální HW čítač nebo matice přístupů → v čisté podobě příliš drahé

6. Aging algoritmus (softwarová simulace LRU)

  • Pro každou stránku n-bitový čítač C (inicializován na 1 při načtení)
  • Periodicky: posun C doprava o 1 bit, nastavení MSB na hodnotu R bitu, reset R bitu
  • Oběť = stránka s nejmenší hodnotou C
  • ✅ Menší režie než LRU, rozumná aproximace
  • ❌ Pamatuje jen omezenou historii (n bitů); nerozliší přesný čas přístupu v jedné periodě
AlgoritmusVýpadků (příklad)SložitostPraxe
OPT6 (minimum)NerealizovatelnýBenchmark
LRU7HW podporaAproximace (Aging)
NRU7SnadnáAno
Clock8Středně složitáUnix SVR4, Linux
Aging9Středně složitáSoftwarové OS
FIFO9JednoducháMéně vhodný
Linux – algoritmus pro náhradu stránek

Linux používá variantu LRU se dvěma oddělenými LRU seznamy pro každý typ stránek: aktivní (active_anon, active_file) a neaktivní (inactive_anon, inactive_file). Anonymní stránky jsou odděleny od file-backed (odlišné vzory přístupu). Fyzická paměť se spravuje pomocí Buddy alokátoru (alokace v mocninách dvou rámců) a Slab alokátoru (malé objekty jádra).

🏗️ Návrh stránkovacích systémů – klíčové otázky

Kdy nahrát stránku?

  • Demand paging – stránka se nahraje až při prvním přístupu (výpadek stránky)
  • Pre-paging – při výpadku se nahraje stránka + několik sousedních (prostorová lokalita) → méně přenosů z disku

Kolik rámců přidělit procesu?

  • Málo rámců → mnoho výpadků stránek → thrashing (zahlcení výměnou stránek)
  • Od určitého počtu se počet výpadků výrazně nemění (= Working Set)
  • Variable-allocation: OS přiděluje rámce podle aktuálních potřeb procesu
Working Set (WS) = množina stránek, ke kterým proces přistupoval v posledním časovém okně. Zajistit, aby WS každého procesu byl v paměti → minimalizace výpadků.

Sdílení paměti:

  • .text segment sdílených knihoven → sdílení v paměti (MAP_SHARED), jen jeden rámec na systém
  • .data sdílených knihoven → MAP_PRIVATE + Copy-on-Write: kopie se vytvoří až při zápisu

Kernel space a KPTI (Meltdown):

Jádro OS je tradičně namapováno do VAS každého procesu (chráněno U bitem = 0). Po útoku Meltdown (2018): KPTI (Kernel Page Table Isolation) – do user space se namapuje pouze nutné minimum jádra; jádro má vlastní oddělené ST. Při systémovém volání se přepíná PTBR.

📌 Shrnutí okruhu 2

  • Stránkování dělí VAS i fyzickou paměť na bloky stejné velikosti (stránky/rámce). Řeší fragmentaci paměti a umožňuje VAS větší než fyzická paměť.
  • Překlad adresy: číslo stránky → index do ST → číslo rámce; fyzická adresa = rámec + offset. MMU provádí překlad hardwarově za pomoci ST uložených v paměti.
  • Víceúrovňová ST šetří paměť tím, že tabulky nižší úrovně existují jen pro používané části VAS. TLB cache akceleruje překlad (typicky >99% hit rate).
  • Výpadek stránky: OS vybere oběť (PRA), odloží ji (pokud dirty), načte požadovanou stránku, aktualizuje ST + TLB.
  • Algoritmy nahrazování: OPT (optimum, nerealizovatelný), LRU (nejlepší aproximace), Clock (praktický), Aging (softwarová aproximace LRU). Linux: varianta LRU s aktivními/neaktivními seznamy.

Kontrolní otázky ke státnicím

1. Jak probíhá překlad virtuální adresy na fyzickou?

Virtuální adresa se rozdělí na číslo stránky a offset. MMU nejprve hledá v TLB (TLB hit → přímý výsledek). TLB miss → prochází stránkovací tabulku v paměti (jednoúrovňová: jeden přístup; víceúrovňová: n přístupů). Pokud P bit = 0 → výpadek stránky → OS obsluha. Fyzická adresa = číslo rámce (z ST) + offset (nezměněn). Překlad se provádí pro každý přístup do paměti transparentně procesem.

2. Proč se používá víceúrovňová stránkovací tabulka?

Jednoúrovňová ST musí mít řádek pro každou stránku VAS, i když je většina VAS nevyužitá – pro 32-bit proces = 4 MB na ST. Víceúrovňová ST existuje v paměti jen pro skutečně používané části VAS (tabulky nižší úrovně se alokují až při potřebě). Pro procesy s řídkým VAS to výrazně šetří paměť. Nevýhoda: pomalejší překlad (více přístupů do paměti → kompenzováno TLB).

3. Co je TLB a proč je potřeba?

TLB (Translation Lookaside Buffer) je rychlá asociativní cache překladů virtuálních na fyzické adresy. Bez TLB by každý přístup do paměti vyžadoval n přístupů do ST (n = počet úrovní). Díky principu lokality je TLB hit ratio typicky >99%, čímž se průměrná latence překladu blíží latenci TLB (~1 ns) namísto přístupu do RAM (~50 ns). Při přepnutí procesu musí OS zneplatnit TLB záznamy (flush) nebo používat ASID.

4. Popište průběh obsluhy výpadku stránky.

1) Ověření platnosti a oprávněnosti přístupu (segfault pokud ne). 2) Nalezení volného rámce – pokud žádný není volný, algoritmus PRA vybere oběť; pokud je oběť dirty (D=1), zapíše se na disk. 3) Zneplatnění ST a TLB záznamu obětovaného rámce ve všech dotčených procesech. 4) Načtení požadované stránky do rámce – ze souboru (file-backed), ze swap souboru, nebo inicializace nulou (anon). 5) Aktualizace ST a TLB. 6) Restart přerušené instrukce.

5. Porovnejte LRU a Clock algoritmus nahrazování stránek.

LRU (Least Recently Used) nahradí stránku, ke které se přistupovalo nejdéle. Je optimální aproximací (využívá časovou lokalitu) ale vyžaduje hardware counter nebo matici přístupů → nepraktické. Clock (hodinový algoritmus) je modifikovaný FIFO s R bitem: ručička prochází kruhovou frontou; stránky s R=1 dostávají „druhou šanci" (R se vynuluje); stránka s R=0 je odstraněna. Prakticky realizovatelný, generuje méně výpadků než FIFO. Aging je softwarová aproximace LRU pomocí n-bitového čítače, který se periodicky posouvá doprava.

6. Jaký je rozdíl mezi jednoúrovňovou a invertovanou stránkovací tabulkou?

Jednoúrovňová ST: každý proces má vlastní tabulku, jeden řádek pro každou stránku VAS → velká paměťová náročnost (škáluje s počtem procesů × velikostí VAS). Invertovaná ST: celý systém má jednu tabulku, jeden řádek pro každý fyzický rámec → konstantní velikost (škáluje jen s fyzickou pamětí). Nevýhoda: překlad pomocí hashování + zřetězení je pomalejší → nutnost TLB. Používána v PowerPC, UltraSPARC.

7. Co je thrashing a jak mu předejít?

Thrashing nastane, když procesu je přiděleno příliš málo fyzických rámců – neustále dochází k výpadkům stránek a OS stráví více času výměnou stránek než samotným výpočtem. Prevence: přidělovat procesu Working Set počet rámců (množina stránek nedávno používaných). Pokud součet WS všech procesů překročí fyzickou paměť, je nutné snížit počet aktivních procesů (swap celých procesů na disk).