Ú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:
Další části:
A můžete také nahlédnout na můj kanál na =)
Pozor! k této přednášce je laborka! podívej se
Procesní API
Prozkoumejme příklad vytvoření procesu v systému UNIX. Dochází k němu prostřednictvím dvou systémových volání. fork() a exec().
Volání fork()

Podívejme se na program, který provádí volání fork(). Výsledek jeho provedení bude následující.

Nejprve vstoupíme do funkce main() a provedeme výstup řetězce na obrazovku. Řetězec obsahuje identifikátor procesu, který se původně nazývá PID nebo identifikátor procesu. Tento identifikátor se používá v UNIXu k adresaci k procesu. Další příkaz vykoná volání fork(). V této chvíli se vytváří prakticky přesná kopie procesu. Pro OS to vypadá tak, že v systému běží jako by dvě kopie jedné a téže aplikace, které se následně ukončí po provedení funkce fork(). Nově vytvořený proces potomek (vzhledem k procesi rodiče, který ho vytvořil) již nebude pokračovat v provádění funkce main(). Je třeba si pamatovat, že proces potomek není přesnou kopií procesu rodiče, má například vlastní adresní prostor, vlastní registry, vlastní ukazatel na vykonávané instrukce a podobně. Tím pádem bude hodnota vrácená volajícímu funkci fork() odlišná. Konkrétně proces rodič obdrží jako návratovou hodnotu PID procesu dítěte, zatímco dítě obdrží hodnotu rovnající se 0. Na základě těchto návratových kódů lze později rozdělit procesy a přimět každý z nich vykonávat svou práci. Při tom vykonávání této aplikace není striktně určeno. Poté, co dojde k rozdělení na 2 procesy, OS začne monitorovat je, stejně jako plánovat jejich pracovní zatížení. V případě provádění na jednojádrovém procesoru bude pokračovat jeden z procesů, v tomto případě – rodič, a poté získá kontrolu proces potomek. Při opětovném spuštění může situace dopadnout jinak.
Volání wait()

Podívejme se na následující program. V tomto programu díky existenci volání wait() proces rodič vždy bude čekat na dokončení práce procesu potomka. V tomto případě získáme striktně určený výstup textu na obrazovku.

Volání exec()

Prozkoumejme volání exec(). Toto systémové volání je užitečné, když chceme spustit zcela jiný program. Zde budeme volat execvp() k spuštění programu wc, který je programem pro počítání slov. Co se tedy stane při volání exec()? Jako argumenty se předávají název spustitelného souboru a některé parametry. Poté dojde k načtení kódu a statických dat z tohoto spustitelného souboru a přepisování vlastního segmentu s kódem. Ostatní oblasti paměti, jako zásobník a halda, jsou znovu inicializovány. Následně OS jednoduše spustí program, předávajíc mu sadu argumentů. Tak jsme nevytvořili nový proces, ale pouze jsme transformovali aktuálně spuštěný program na jiný spuštěný program. Po provedení volání exec() v potomkovi vzniká dojem, že původní program se vůbec nespustil.
Takové ztížení spuštění je naprosto normální pro shell prostředí Unixu a umožňuje této shellové skriptovací prostředí vykonávat kód po volání fork(), ale před voláním exec(). Příkladem takového kódu může být přizpůsobení prostředí shellu potřebám spouštěného programu před jeho vlastním spuštěním.
Shell — je pouze uživatelský program. Zobrazuje vám řádek výzvy a čeká, až do něj něco napíšete. Ve většině případů, pokud tam napíšete název programu, shell jeho umístění najde, zavolá metodu fork() a poté, aby vytvořil nový proces, zavolá některou z variant exec() a čeká na jeho vykonání pomocí volání wait(). Jakmile proces-potomek skončí, shell se vrátí z volání wait() a opět zobrazí řádek výzvy a bude čekat na zadání dalšího příkazu.
Oddělení fork() & exec() umožňuje shellu provádět následující věci, například:
wc file > new_file.
V tomto příkladu je výstup programu wc přesměrován do souboru. Způsob, jakým shell toho dosahuje, je dost jednoduchý — při vytváření procesu-dítěte před voláním exec(), shell uzavírá standardní výstupní proud a otvírá soubor new_file, čímž je veškerý výstup z dále spouštěného programu wc přesměrován do souboru místo obrazovky.
Unixové potrubí jsou realizována podobným způsobem, s rozdílem, že používají volání pipe(). V tomto případě bude výstupní proud procesu připojen k frontě potrubí umístěné v jádře, ke které bude připojen vstupní proud jiného procesu.
Zdroj: habr.com
