Úvod do operačních systémů
Ahoj, Habr! Rád bych vám představil sérii článků-překladů jedné zajímavé literatury, kterou považuji za hodnotnou — OSTEP. Tento materiál se hlouběji zabývá prací unixových operačních systémů, konkrétně prací s procesy, různými plánovači, pamětí a dalšími komponentami, které tvoří moderní OS. Originál všech materiálů můžete vidět tady . Upozorňuji, že překlad byl proveden neprofesionálně (poměrně volně), ale doufám, že jsem zachoval celkový smysl.
Laboratorní práce k tomuto předmětu najdete zde:
- originál:
- originál:
- moje osobní adaptace:
Další části:
A můžete také nahlédnout na můj kanál na =)
Podívejme se na nejzákladnější abstrakci, kterou operační systém poskytuje uživatelům: proces. Definice procesu je docela jednoduchá — je to běžící program. Program sám o sobě je neživá věc, nacházející se na disku — je to soubor instrukcí a možná nějaká statická data, která čekají na spuštění. Právě operační systém bere tyto bajty a spouští je, přetvářejíc program na něco užitečného.
Nejčastěji chtějí uživatelé spouštět více než jeden program zároveň, například můžete spustit na svém notebooku prohlížeč, hru, multimediální přehrávač, textový editor a podobně. Ve skutečnosti typický systém může spouštět desítky a stovky procesů současně. Tento fakt činí systém snadnějším na používání, nikdy se nemusíte starat o to, zda je CPU volné, prostě spouštíte programy.
Odtud vyvstává problém: jak zajistit iluzi mnoha CPU? Jak může operační systém vytvořit iluzi téměř nekonečného počtu CPU, i když máte pouze jeden fyzický CPU?
Operační systém vytváří tuto iluzi pomocí virtualizace CPU. Spouštěním jednoho procesu, poté jeho zastavením, spuštěním jiného procesu a tak dále, může operační systém udržovat iluzi, že existuje mnoho virtuálních CPU, ačkoliv ve skutečnosti bude pouze jeden nebo několik fyzických procesorů. Tato technika se nazývá dělění prostředků CPU v čase. Tato technika umožňuje uživatelům spouštět tolik současných procesů, kolik chtějí. Cena za toto řešení je výkon – protože pokud CPU sdílí několik procesů, každý proces bude zpracováván pomaleji.
Pro realizaci virtualizace CPU, a zejména pro to, aby to dělalo dobře, operační systém potřebuje jak nízkoúrovňovou, tak vysokoúrovňovou podporu. Nízkoúrovňová podpora se nazývá mechanismy — to jsou nízkoúrovňové metody nebo protokoly, které implementují potřebnou část funkčnosti. Příkladem takové funkčnosti je přepínání kontextu, které dává operačnímu systému možnost zastavit jeden program a spustit na procesoru jiný program. Toto dělení v čase je realizováno ve všech moderních operačních systémech.
Na vrcholu těchto mechanismů se nachází určité logické prvky zakotvené v OS ve formě "politik". Politika je určité rozhodovací algoritmus operačního systému. Tyto politiky například rozhodují, kterou program je třeba spustit (ze seznamu příkazů) jako první. Například tuto úlohu vyřeší politika nazývaná plánovač (scheduling policy) a při výběru rozhodnutí se řídí takovými daty jako: historie spuštění (který program byl spuštěn nejdéle za poslední minutu), jakou zátěž vykonává tento proces (jaké typy programů byly spuštěny), metriky výkonnosti (je systém optimalizován pro interaktivní interakci nebo pro propustnost) a tak dále.
Abstrakce: proces
Abstrakce běžícího programu, který provádí operační systém je to, co nazýváme proces. Jak bylo již zmíněno, proces je prostě běžící program, v jakémkoli okamžiku času. Program, s pomocí kterého můžeme získat souhrnné informace z různých zdrojů systému, a na které se tento program obrací nebo které se dotýká během svého provádění.
Pro pochopení složek procesu je třeba rozumět stavům systému: co program může číst nebo měnit během svého provozu. V jakémkoli okamžiku je nutné vědět, které prvky systému jsou důležité pro provádění programu.
Jedním z očividných prvků stavu systému, které zahrnuje proces, je paměť. Instrukce se nacházejí v paměti. Data, která program čte nebo zapisuje, se také nacházejí v paměti. Takže paměť, kterou proces může adresovat (takzvaný adresní prostor), je částí procesu.
Také součástí stavu systému jsou registry. Mnoho instrukcí je zaměřeno na to, aby změnily hodnotu registrů nebo aby četly jejich hodnotu, a tím se registry také stávají důležitou součástí práce procesu.
Je třeba poznamenat, že stav stroje se také vytváří z některých speciálních registrů. Například, IP — ukazatel instrukce je ukazatel na instrukci, kterou program provádí v současné době. Existuje také stack pointer a s ním spojený frame pointer, které se používají k řízení: parametrů funkcí, místních proměnných a návratových adres.
Nakonec se programy často obracejí na ROM (trvalou paměť). Tyto informace o „I/O“ (vstupu-výstupu) by měly zahrnovat seznam souborů, které jsou aktuálně otevřeny procesem.
Procesní API
Abychom lépe pochopili fungování procesu, podívejme se na příklady systémových volání, která by měla být zahrnuta do jakéhokoli rozhraní operačního systému. Tyto API jsou v určité podobě dostupná na každém OS.
● Vytvořit (vytváření): V OS musí být nějaká metoda, která umožňuje vytváření nových procesů. Když zadáte příkaz do terminálu nebo spustíte aplikaci dvojitým kliknutím na ikonu, je odeslána žádost operačnímu systému o vytvoření nového procesu a následné spuštění uvedeného programu.
● Odstranit: Pokud existuje rozhraní pro vytváření procesu, operační systém by měl také nabízet možnost násilného ukončení procesu. Většina programů se přirozeně spustí a ukončí sama během svého běhu. Jinak by měl mít uživatel možnost je ukončit, takže rozhraní pro zastavení procesu by nebylo na škodu.
● Čekání (čekání): Někdy je užitečné počkat na dokončení procesu, a tak jsou k dispozici některá rozhraní, která tuto možnost poskytují.
● Různé řízení (rozmanité řízení): Kromě ukončení a čekání procesu existují také další různorodé kontrolní metody. Například většina OS poskytuje možnost mražení procesu (pozastavení jeho provádění na určitou dobu) a následné obnovení (pokračování v provádění).
● Status (stav): Existují různá rozhraní pro získání informací o stavu procesu, jako je doba jeho běhu nebo v jakém stavu se právě nachází.

Vytváření procesu: detaily
Jedna z zajímavých věcí je, jak přesně se programy transformují na procesy. Zvláště, jak operační systém spouští a řídí program. Jak konkrétně se proces vytváří.
Nejprve musí operační systém načíst kód programu a statická data do paměti (do adresního prostoru procesu). Programy jsou obvykle umístěny na disku nebo pevném disku v nějakém spustitelném formátu. Proces načítání programu a statických dat do paměti vyžaduje od operačního systému schopnost číst tyto byty z disku a umístit je někde v paměti.
V raných operačních systémech se proces načítání prováděl neúnavně (eagerly), což znamená, že kód byl načten do paměti úplně dříve, než byla program spuštěn. Moderní operační systémy to dělají líně (lazily), tj. načítají kousky kódu nebo dat pouze tehdy, když jsou během provádění programu potřeba.
Po načtení kódu a statických dat do paměti musí operační systém provést ještě několik věcí před spuštěním procesu. Musí být alokováno určité množství paměti pro zásobník. Programy používají zásobník pro lokální proměnné, parametry funkcí a adresy návratu. Operační systém tuto paměť alokuje a předává ji procesu. Zásobník může být také alokován s některými argumenty, konkrétně naplněním parametrů funkce main(), například polem argc a argv.
Operační systém může také alokovat určité množství paměti pro haldu (heap) programu. Halda se používá programy pro explicitně požadované dynamicky alokované údaje. Programy si tuto paměť žádají zavoláním funkce malloc() a explicitně ji uvolňují zavoláním funkce free(). Halda je potřebná pro struktury dat jako jsou: propojené seznamy, hash tabulky, stromy a další. Na začátku se pro haldu alokuje malé množství paměti, ale s postupem času může halda během běhu programu vyžádat více paměti, prostřednictvím knihovního API voláním malloc(). Operační systém je zapojen do procesu přidělování více paměti, aby pomohl vyhovět těmto požadavkům.
Operační systém také provede inicializační úkoly, zejména ty, které se týkají vstupu a výstupu. Například v systémech UNIX má každý proces ve výchozím nastavení 3 otevřené souborové popisovače pro standardní vstup, výstup a chyby. Tyto popisovače umožňují programům číst vstup z terminálu a také vypisovat informace na obrazovku.
Tímto způsobem operační systém připravuje platformu pro vykonávání procesu, když načítá kód a statické údaje do paměti, vytváří a inicializuje zásobník a vykonává další úkoly související s vykonáváním vstupně-výstupních úloh. Nakonec zbývá poslední úkol: spustit program prostřednictvím jeho vstupního bodu, nazvaného funkce main(). Při přechodu k vykonání funkce main() operační systém předává řízení CPU nově vytvořenému procesu, čímž se program začíná vykonávat.
Stav procesu
Nyní, když máme určité porozumění tomu, co je to proces a jak je vytvořen, vyjmenujme stavy procesu, ve kterých se může nacházet. V nejjednodušší formě může být proces v jednom z těchto stavů:
● Dokončeno. Ve stavu RUNNING se proces vykonává na procesoru. To znamená, že dochází k vykonávání instrukcí.
● Ready. Ve stavu READY je proces připraven k spuštění, ale z nějakého důvodu ho operační systém v daném okamžiku nevykonává.
● Blocked. Ve stavu BLOCKED vykonává proces nějaké operace, které mu neumožňují být připraven k vykonání, dokud nenastane nějaká událost. Jedním z běžných příkladů je, když proces inicializuje operaci IO, stává se blokovaným a tím pádem může jiný proces používat procesor.

Tyto stavy si můžeme představit jako graf. Jak vidíme na obrázku, stav procesu se může mezi RUNNING a READY měnit podle uvážení operačního systému. Když se stav procesu změní z READY na RUNNING, znamená to, že proces byl naplánován. Naopak, když je vyřazen z plánování. V okamžiku, kdy se proces stane BLOCKED, například při inicializaci operace IO, operační systém ho v tomto stavu udrží do doby, než nastane nějaká událost, například dokončení IO. V tomto okamžiku přechází do stavu READY a případně okamžitě do stavu RUNNING, pokud to tak operační systém rozhodne.
Podívejme se na příklad toho, jak dva procesy procházejí těmito stavy. Na začátek si představme, že oba procesy jsou spuštěny a každý používá pouze CPU. V tomto případě budou jejich stavy vypadat takto.

V následujícím příkladu první proces po určité době práce požádá o IO a přechází do stavu BLOCKED, což poskytuje druhému procesu možnost spuštění (OBR 1.4). OS vidí, že proces 0 nepoužívá CPU a spouští proces 1. Během běhu procesu 1 — IO končí a stav procesu 0 se mění na READY. Nakonec proces 1 skončí, a po jeho dokončení se spustí proces 0, vykonává se a končí svou práci.

Datová struktura
OS sama o sobě je program a stejně jako jakýkoli jiný program má několik klíčových datových struktur, které sledují různé relevantní kusy informací. Pro sledování stavu každého procesu v OS bude udržována určitá procesní seznam pro všechny procesy ve stavu READY a některé další informace pro sledování procesů, které se aktuálně vykonávají. Také, OS musí sledovat a blokované procesy. Po dokončení IO je OS povinna probudit potřebný proces a převést ho do stavu připravenosti k spuštění.
Tak například OS musí uchovávat stav registrů procesoru. V okamžiku zastavení procesu je stav registrů uložen v adresním prostoru procesu a v okamžiku obnovení jeho činnosti — obnoví hodnoty registrů a tímto způsobem pokračuje v provádění tohoto procesu.
Kromě stavů ready, blocked, running existují také některé další stavy. Někdy v okamžiku vytvoření může mít proces stav INIT. Nakonec může být proces umístěn do stavu FINAL, když již skončil, ale informace o něm ještě nebyla vymazána. V systémech UNIX se tento stav nazývá proces-zombi. Tento stav je užitečný pro případy, kdy rodičovský proces chce zjistit návratový kód potomka, například obvykle 0 signalizuje úspěšné dokončení, a 1 o chybu, nicméně programátoři mohou dělat další kódy výstupu, signalizující různé problémy. Při ukončení rodičovský proces provede poslední systémový volání, například wait(), aby čekal na dokončení práce procesu-potomka a signalizoval OS, že může vyčistit jakákoli data spojená se dokončeným procesem.

Klíčové body přednášky:
● Proces Hlavní abstrakce pracujícího programu v OS. V každém okamžiku lze proces popsat podle jeho stavu: obsahu paměti ve jeho adresním prostoru, obsahu registrů procesoru, včetně ukazatele instrukcí a ukazatele zásobníku, stejně jako informací o IO, například otevřenými soubory, které se čtou nebo zapisují.
● Procesní API se skládá z volání, které mohou programy provádět ve vztahu k procesům. Obvykle se jedná o volání pro vytváření, odstraňování nebo jiné.
● Proces se nachází v jednom z mnoha stavů, včetně běžícího, připraveného nebo blokovaného. Různé události, jako je plánování, vyloučení z plánování nebo čekání, mohou přenést stav procesu z jednoho na druhý.
● Seznam procesů obsahuje informace o všech procesech v systému. Každý záznam v něm se nazývá kontrolní blok procesu, který je ve skutečnosti strukturou obsahující všechny potřebné informace o konkrétním procesu.
Zdroj: habr.com
