Operační systém, jádro, procesy

Obsah této lekce


Operační systém (OS) jeho význam a určení

Operační systém je sada programů (software) umožňujících co nejefektivnější využití počítače. Základním úkolem operačního systému zabezpečit podporu a zpracování aplikačních programů.

Úkolem operačního systému je poskytovat programátorům aplikací jednotný přístup zejména k datům na disku, k síti a vstupním a výstupním zařízením (klávesnici, myši (polohovací zařízení), joysticku, grafické kartě, tiskárně ap.). Moderní operační systémy umožňují multitasking (současný běh více programů) a obsahují velmi vyvinutou a konfigurovatelnou podporu ostatních programů a mají složitější strukturu.

Existují v zásadě tři pojetí OS:

  1. OS = JOS. systémové pojetí
  2. OS = JOS + systémové služební programy dodané jako celek. obchodní pohled
  3. OS = JOS + veškeré programy používané v systému + způsob práce s nimi. organizační hledisko

Budeme používat tzv. programátorské pojetí OS: JOS + systémově závislé knihovny, které transformují požadavky tzv. aplikačních programů na volání konkrétních služeb poskytovaných JOS prostřednictvím přirozeného API (Application Programmer’s Interface).

Knihovny vytvářejí programátorské rozhraní, které umožňuje psát programy přenositelné mezi různými platformami s týmž (či analogickým) operačním systémem.

Příklady operačních systémů pro osobní počítače


Jádro operačního systému (JOS)

Základní složkou operačního systému je jeho jádro. JOS je soustava vzájemně propojených a spolupracujících programových modulů nacházejících se v operační paměti.

Existují dvě téměř nezávislé funkce JOS: Operační systém jako rozšíření počítače Strojový jazyk je obtížný a programování v něm je neefektivní. JOS poskytuje snazší programovací prostředí tím, že exportuje služby pro rutinní akce (např. V/V operace) se standardizovanými volacími sekvencemi. Tím vzniká na povrchu JOS programátorské rozhraní (native API), které lze považovat za virtuální počítač s rozšířenými schopnostmi. Operační systém jako správce systémových zdrojů

Při pohledu „zdola“, hardware počítače sestává z mnoha komponent, které musí být systémem spravovány a jejich užití evidováno (např. několik programů se snaží tisknout současně).

Má-li počítač více uživatelů, stává se potřeba ochrany dat, souborů, V/V zařízení a dalších systémových zdrojů zcela nezbytnou.

Účel JOS

  1. JOS zvyšuje účinnost využití technických prostředků prostřednictvím jejich sdílení a zapůjčování jednotlivým výpočetním procesům.
  2. JOS poskytuje služby aplikačním programům, vzájemně je izoluje a zprostředkuje jejich spolupráci.

Mikrojádro - Microkernel

Mikrojádro je jádro operačního systému, které obsahuje jen základní funkčnost pro běh operačního systému. Typicky obsahuje jen správu paměti a správu řízení procesů. Ostatní funkční bloky jako správa souborového systému, správa síťových rozhraní, apod. je v operačním systému s mikrojádrem řešena formou samostatných procesů.

Výhodou tohoto uspořádání je mimo jiné vyšší schopnost operačního systému se udržet v chodu i poté co dojde v některém z podsystémů k závažné chybě.


Typy OS (jedno a víceúlohové)


Výpočetní proces


Multitasking

Termínem multitasking (z angličtiny, multi = mnoho, task = úloha) se v informatice označuje schopnost počítače, resp. operačního systému provádět (přinejmenším zdánlivě) několik úloh současně. Dnešní operační systémy jsou typicky víceúlohové – sem patří např. Microsoft Windows či Linux. Naopak MS-DOS je příkladem jednoúlohového systému, na kterém vždy běží pouze jediný program a teprve po jeho ukončení je možno spustit jiný.

Multitasking může být realizován mnoha způsoby. Základní dělení je na skutečný multitasking, kdy je hardware počítače opravdu schopen v každém okamžiku zpracovávat více úloh současně, a na zdánlivý multitasking, kdy se dojmu současného běhu více programů dosahuje tím, že se tyto programy velice rychle v běhu střídají, ale v každý jednotlivý okamžik běží pouze jediná úloha. Opravdu 100% skutečný multitasking se v praxi přiliš neobjevuje, běžné operační systémy podporují druhou zmiňovanou techniku, ale pokud je počítač vybaven více procesory, jsou úlohy mezi tyto procesory rozděleny tak, aby alespoň některé úlohy mohly běžet současně.

Podle způsobu přidělování a odebírání časových kvant se rozlišují dva základní způsoby zdánlivého multitaskingu: kooperativní multitasking a preemptivní multitasking.

Kooperativní multitasking

Kooperativní multitasking (též nepreemptivní) vyžaduje aktivní spoluúčast běžících úloh. Každá úloha je povinna dostatečně často voláním systémové funkce předat řízení zpět operačnímu systému, který díky tomu spustit jinou úlohu, která se po chvíli opět dobrovolně vzdá procesoru atd.

Kooperativní multitasking používaly například verze Microsoft Windows do Windows 3.x1 nebo Mac OS před Mac OS X. Zatímco je v moderních systémech kooperatvní multitasking na ústupu, například RISC OS ho stále používá.

Výhodou řešení je jednodušší implementace operačního systému, podstatnou nevýhodou skutečnost, že chybně naprogramovaná úloha, která nevrátí řízení zpět operačnímu systému, způsobí úplné zastavení systému i ostatních úloh.

Preemptivní multitasking

Při preemptivním multitaskingu o přidělování a odebírání procesoru jednotlivým úlohám plně rozhoduje operační systém. V pravidelných intervalech (typicky zhruba 10 ms) přeruší provádění běžícího programu, vyhodnotí aktuální situaci (které úlohy žádají o přidělení procesoru, jejich priority atd.) a nechá běžet buď opět úlohu, kterou přerušil, nebo jinou úlohu, která má zájem o přidělení procesoru. I v preemptivním multitaskingu však může úloha dobrovolně požádat o přepnutí kontextu, vzdát se zbytku svého kvanta, např. pokud chce na něco čekat (např. na pomalou vstupní/výstupní operaci, jako je čtení dat z pevného disku).

Preemptivní multitasking používají MS Windows od verze 95 (v řadě NT je od začátku), Mac OS od Mac OS X, jádro Unix nebo Linux od svého vzniku.

Výhodou tohoto řešení je, že nedochází k „zatuhnutí“ počítače, neboť i v případě, že úloha zhavaruje, odebere operační systém dané úloze řízení a přidělí časové kvantum ostatním úlohám. Nevýhodou je složitější implementace operačního systému a vyšší nároky na hardwarové vybavení počítače.

Atomické operace

U některých operací musí být zaručeno, že proběhnou celé, že jejich provádění nebude v polovině přerušeno např. přepnutím na jinou úlohu. Takové operace se označují jako atomické. Každá atomická operace musí proběhnout buď celá, nebo vůbec. Příkladem atomické operace je např. semafor či jiné synchronizační primitivum. Mezi zjištěním jeho stavu a následným přepnutím nesmí být úloha přerušena, protože úloha, která by pak dostala řízení, mohla mezitím stav semaforu změnit a došlo by k chybnému chování.

Příklad:

U kooperativního multitaskingu je atomicita takových operací zaručena prostě tím, že se v jejím průběhu nevolají systémové funkce, které by umožnily přepnutí kontextu. Jelikož nikdy jindy k přepnutí dojít nemůže, je tím zaručena správná funkce. U preemptivního multitaskingu je naopak potřeba takovému nevhodnému přepnutí explicitně bránit. U skutečného multitaskingu, tzn. při práci na více procesorech se atomicita operací zajišťuje s pomocí hardwarových prostředků a podpory procesoru; nejdůležitějším faktem v tomto případě je, že (přinejmenším některé) jednotlivé instrukce procesoru jsou samy o sobě atomické. Poznámka: Windows/386 2.x, vydaných v roce 1988, a pozdějších verzích Windows bylo možné samostatně spouštět více DOS aplikací, které běžely v preemptivním multitaskingu, ale vlastní Windows aplikace běžely v kooperativním multitaskingu.


Správa procesoru a procesů


Instrukční soubor


Způsoby adresování


Assembler

Assembler je programovací jazyk velice blízký strojovému kódu, též se nazývá jazyk symbolických adres. Název assembler se používá i pro překladač, který tento jazyk překládá do strojového kódu.

Assembler je programovací jazyk nejnižší úrovně, je strojově závislý a v podstatě každý procesor má svůj vlastní odlišný assembler.

Assembler tvoří pouze zástupné symboly, které přímo odpovídají strojovému kódu. Mnemotechnické symboly se mnohem lépe zapamatovávají, než nicneříkající čísla strojového kódu. Další výhodou je používání symbolů pro adresy dat a instrukcí, které se při překladu nahradí skutečnou aktuální adresou.

Například počítač s odpovídajícím procesorem umí provést následující strojovou instrukci procesoru x86/i386 (např. Intel 80386):

10110000 01100001

Pro programátora je ale mnohem snazší používat ekvivalentní zápis v assembleru:

mov  al, 61h

který znamená přesun hexadecimální hodnoty 61 (97 dekadicky) do registru procesoru pojmenovaného „al“. Název instrukce „mov“ (zkratka anglického slova move – přesun) je následován seznamem parametrů. Tak vypadá typická instrukce assembleru.

Překladače také obvykle poskytují tzv. makra, která nahrazují často používané posloupnosti jednotlivých instrukcí. Další součástí assembleru bývají direktivy (také pseudoinstrukce), které umožňují ovlivňovat překlad, vyhrazovat místo v paměti pro proměnné nebo manipulovat s čítačem adres.

Terminologie

Anglické slovo assembler znamená sestavovatel a původně označuje pouze překladač, program, který sestavuje strojový kód. Programovací jazyk zpracovávaný takovým překladačem se v angličtině jmenuje prostě assembly language.

Exaktní česká terminologie z tohoto vychází a jako assembler označuje pouze překladač, zatímco programovací jazyk se nazývá výhradně jazyk symbolických adres, kterýžto výraz popisuje základní nabízenou výhodu – odstranění nutnosti ručně přepočítávat veškeré adresy při změně programu. V praxi se ovšem toto pravidlo nepoužívá a termín assembler se zcela běžně používá pro pojmenování programovacího jazyka.

Překladače assembleru

Existuje velké množství překladačů assembleru, pro x86 například:


Překlad a interpretace

Počítačové programy jsou obecně psány v programovacích jazycích. Procesor však příkazům programovacích jayků nerozumí. Zdrojový kód programovacího jazyka tedy musí být neprve preden dop formy srozumitelné procesoru. Toto je možné provést buď najednou kompilací nebo postupně příkaz po příkazu, za behu programu, pomocí interpreteru.

Nutno ovšem podoktnout, že v poslední době se stále více prosazujé kombinace obou metod: just-in-time kompilace. Jedná se v podstatě o interpret, který překládá do strojového kódu pouze bloky zrdojového kódu, bezprostředně před jejich vykonáním.

Další tendencí, která se v poslední době prosazuje je urcitá předkompilace do optimalizového kódu - nikoliv však do přímo do zdrojo kódu. Tento princip se uplatňue především u jazyka Java - zde se tentop mezivýsledek nazýva byte-code nebo u technologie .NET - peziprodukt se nazývá (Microsoft) Intermidiate Language - MSIL.

Překlad

Překladač (též kompilátor, anglicky compiler z to compile – sestavit, zpracovat) je všeobecně stroj, respektive program, provádějící překlad z nějakého vstupního jazyka do jazyka výstupního. Nejčastěji bývá tento pojem používán v programování, kde označuje nástroje překládající algoritmus zapsaný ve vyšším programovacím jazyce do jazyka strojového, nebo spíše do strojového kódu. (O překladačích mezi přirozenými jazyky viz strojový překlad.)

První kompletní překladač byl vyvinut v roce 1957 ve společnosti IBM a byl pro jazyk Fortran. Jazyk COBOL měl jako první na světě překladač pro více architektur, což zvýšilo jeho přenositelnost. Tyto překladače byly vytvořeny v assembleru. Jazyky, které umožňují napsat překladač, který přeloží sám sebe, se označují jako self-hosting („samonosné“) a prvním takovým se stal LISP, jehož překladač byl vytvořen v roce 1962 na MIT. Ovšem použití jazyků vyšší úrovně pro psaní překladačů se stalo běžné až po roce 1970, kdy byly překladače C a Pascalu vytvořeny v těchto jazycích.

Překladač lze navrhnout mnoha způsoby, nejčastěji bývá rozdělen na dvě části. První je závislá na vstupním jazyce (tzv. front-end) a druhá závisí na cílové architektuře (tzv. back-end). Mezi těmito částmi program existuje ve formě tzv. mezikódu (bytekódu).

Typický překladač bývá rozvržen do následujících jednotek:

Lexikální analyzátor

Lexikální analyzátor je první jednotka překladače, která má za úkol relativně jednoduchým způsobem získat ze vstupního zdrojového textu tzv. lexému (některý základní prvek příslušného jazyka, např. klíčové slovo „if“) a tu pak zasílá syntaktickému analyzátoru.

Všechny možné lexémy jsou ve vstupním jazyce popsány pomocí regulárních výrazů. V kompilátoru se pro jejich rozpoznávání používá konečný automat. Kromě samotné lexémy se do syntaktického analyzátoru posílají další související údaje, např. jakého druhu je daná lexéma (operátor, klíčové slovo, identifikátor, …), u identifikátorů odkaz do tabulky identifikátorů (pro urychlení překladu, překladač poté nemusí pracovat s textem, ale pouze s číselným označením) apod. Celý soubor údajů předávaný z lexikálního do syntaktického analyzátoru se označuje anglickým termínem token (známka, odznak).

Syntaktický analyzátor

Syntaktický analyzátor (v angličtině označovaný jako parser, z to parse – rozebrat, oddělovat, udělat větný rozbor) se dá považovat za mozek překladače, protože provádí samotnou analýzu vstupního jazyka. Úkolem syntaktického analyzátoru je rozpoznat, zda je program zapsán správným způsobem, např. zda na úvodní „begin“ bude navazovat někde koncové „end“, ale také určuje, v jakém pořadí se budou provádět jednotlivé části příkazů, např. u „x + y*z“ rozpozná a určí, že nejdříve se bude násobit a pak až sčítat, nebo u vnořených příkazů zajistí, že nejdříve se vyhodnotí parametr funkce a teprve pak se daná funkce zavolá.

Úloha syntaktického analyzátoru je o něco složitější než úloha lexikálního analyzátoru, neboť narozdíl od regulárního jazyka lexém musí syntaktický analyzátor rozpoznávat složitější bezkontextový jazyk (typicky LL(1) či LR(1)). Syntaktický analyzátor k tomu musí vyhodnocovat vstup v delším záběru.

Výsledkem práce syntaktického analyzátoru je tzv. syntaktický strom (anglicky „parse tree“), který popisuje strukturu vstupního programu.

Jedním z nejjednodušších způsobů konstrukce syntaktického analyzátoru je tzv. metoda rekurzivního sestupu. V této metodě se pomocí rekurzivních volání konstruuje syntaktický strom, přičemž struktura implementace překladače odráží strukturu příslušné gramatiky. Pokud je v gramatice na daném místě neterminální symbol, v překladači mu odpovídá (rekurzivní) volání funkce reprezentující příslušný neterminál. Tato rekurzivní volání končí u terminálních symbolů (lexémů jazyka), které jsou přečteny a uloženy do syntaktického stromu.

Sémantický analyzátor

Sémantický analyzátor zpracovává syntaktický strom a provádí analýzu významu jednotlivých operací. Narozdíl od syntaktického analyzátoru, který provádí kontrolu správnosti struktury programu čili zápis programového kódu, sémantický analyzátor provádí kontrolu správnosti operací. V tomto mechanismu se odehrává veškerá typová kontrola a různé konverze. Prací sémantického analyzátoru je např. analýza typů výrazů na levé a pravé straně přiřazovacího příkazu, přičemž musí zabránit např. přiřazení hodnoty s plovoucí řádovou čárkou do proměnné s pevnou řádovou čárkou.

Vstupem sémantického analyzátoru je syntaktický strom, který vygeneroval syntaktický analyzátor, výstupem je opět syntaktický strom, ale s doplněnými dodatečnými informacemi a s provedenými konverzemi.

Překladač do mezikódu

V této fázi se ze syntaktického stromu generuje kód, tzn. strukturovaný syntaktický strom se transformuje do lineární posloupnosti instrukcí. Obvykle se však negeneruje přímo kód cílového jazyka, ale kód v nějakém pomocném jazyce, tzv. mezikód. Cílem tohoto kroku je ulehčit následující operace, které by v cílovém jazyce mohly být komplikované. Tohoto přístupu se s výhodou používá pro tvorbu optimalizátorů efektivity kódu. Existují však i překladače bez fáze překladu do mezikódu, překladající přímo do cílového jazyka.

Jednou z možností návrhu mezikódu je tzv. tříadresný kód. Instrukce tříadresného kódu mají jednotnou strukturu, pracují nad adresami dvou vstupních operandů a jednoho výsledkového operandu, např. Id1 = Id2 * 5. Nejtypičtější formou mezikódu však je kód nějakého zásobníkového počítače.

Překladem do mezikódu končí část závislá na zdrojovém programovacím jazyku (front-end), zbytek překladače (back-end) může být dokonce shodný pro překladače různých jazyků (tak je navržen např. překladač GCC).

Jako mezikód se používá i jazyk symbolických adres, někdy též nepřesně nazývaný Assembler (ovšem s omezením na cílovou architekturu).

Optimalizátor

Optimalizátor mezikód různě transformuje, přičemž cílem je urychlení výsledného kódu. Ve vygenerovaném kódu se mohou např. vyskytovat cyklická přiřazení typu:

Id1 = 5 + Id2
Id3 = Id1

takovýto kód optimalizátor přepíše na:

Id3 = 5 + Id2

Také se může vyskytovat tzv. mrtvý kód, tedy kód, který se nikdy nevykoná, například:

while (false)
{
  ...
}

takový kód optimalizátor zcela zahodí. Na výstupu optimalizátoru je stále mezikód.

Generátor kódu

V poslední fázi se z mezikódu generuje výstupní kód, program v cílovém jazyce. Cílovým jazykem nejčastěji bývá přímo strojový kód.

Interpret

Interpret je speciální software, který umožňuje spouštět počítačové programy vytvořené ve skriptovaném programovacím jazyku. Tato činnost je podobná emulaci s tím rozdílem, že emulátor obvykle simuluje činnost existujícího hardwaru, zatímco interpret nejčastěji program spouští nad virtuálním strojem.

V praxi nejsou rozdíly zcela jednoznačné. Například Java Virtual Machine, virtuální stroj nad kterým běžel kód Javy, už má hardwarovou implementaci.

Virtuální stroj má často zásobníkovou architekturu.

Příklady interpretovaných jazyků


Klíčová slova

MIDI - Musical Insturments Digital Interface - rozhraní pro připojení hudebních nástorjů k počítači

Zdroje a další odkazy

33OSA - výukové matriály přemětu Operační systémy a jejich aplikace (CVUT FEL)
Wikipedia - témata podle jednotlivých podkapitol