Základní pojmy

Následující odstavce vás letmo provedou základními pojmy, jejichž znalost je nutná pro pochopení obsahu dalších částí. Text této části nezabíhá do přílišných podrobností, ale klade si za cíl, abyste si o jednotlivých pojmech utvořili rámcovou představu. Podrobnější popis většiny z nich najdete v některé z dalších kapitol. S většinou zde vysvětlených termínů a principů se setkáte i při studiu jiných operačních systémů.

Procesor, úrovně oprávnění a systémová volání

Procesor je hlavním nástrojem operačního systému, který tento malý čip využívá k různým vlastním výpočtům a dává jej k dispozici i aplikacím, jež vy jako uživatel spouštíte. Procesor v sobě implementuje řadu mechanismů, kterých operační systém využívá například pro zaručení ochrany hardwaru před zneužitím zákeřným programem či pro ochranu kódu svého jádra před nežádoucími modifikacemi.

Mezi tyto mechanismy patří možnost vykonávat kód programu na několika úrovních oprávnění, přičemž každá z nich přesně definuje, které operace jsou povoleny a které zakázány. Omezení se vztahuje například na druhy instrukcí, jež lze na jednotlivých úrovních použít. Například procesory Intel kompatibilní s architekturou x86 mají v sobě zabudovány čtyři různé úrovně oprávnění. Tyto úrovně se často označují jako ring módy (rings) a obvykle se číslují od 0 do 3. Čím nižší číslo, tím větší volnost má kód běžící na dané úrovni. Program vykonávaný na úrovni Ring 0 má dovoleno využít veškeré možnosti, které mu instrukční sada procesoru nabízí – může přímo komunikovat s hardware, ovládat chování procesoru či měnit obsah důležitých systémových datových struktur, jako je tabulka vektorů přerušení či globální tabulka segmentů. Úrovně Ring 1 a Ring 2 již některé instrukce vykonávat nepovolují a kódu běžícímu na úrovni Ring 3 procesor neumožňuje žádným způsobem přímo měnit nastavení, která by mohla ovlivnit chování operačního systému. Úroveň Ring 3 například nedovoluje přímou komunikaci s hardware. Z historických důvodů Windows používají pouze úrovně Ring 0, na které běží veškerý kód jádra a ovladačů, a Ring 3, která slouží pro vykonávání kódu normálních aplikací (též označovaných jako procesy). Obecně se úroveň, která poskytuje nejvyšší možná oprávnění, označuje jako režim jádra (kernel mode). Naopak úroveň kladoucí nejvyšší omezení na vykonávaný kód se nazývá uživatelský režim (user mode).

Běžné aplikace samozřejmě nevystačí s úrovní Ring 3, protože potřebují čas od času komunikovat s okolím – číst a zapisovat na pevný disk, posílat data po síti nebo kreslit na obrazovku. Protože úroveň Ring 3 z bezpečnostních důvodů neumožňuje přímou komunikaci s hardware, byl do procesoru implementován mechanismus, který aplikaci umožňuje přejít za určitých podmínek na úroveň Ring 0 a tam vyřídit vše potřebné. Aplikace samozřejmě nemůže sama určit, který blok kódu bude v režimu jádra vykonán; to určuje jádro při zavádění operačního systému.

Aplikace může pouze požádat jádro (zavolat systém – provést systémové volání), aby pro ni vykonalo příslušnou operaci, jenž není v Ring 3 povolena. Jádro může požadavku aplikace vyhovět, ale také nemusí. Operační systém totiž implementuje vlastní bezpečnostní model, který umožňuje například chránit důležité soubory před neoprávněnou manipulací. Pokud se nějaký program spuštěný uživatelem s příliš nízkými právy pokusí k chráněnému souboru získat přístup, jádro obdrží požadavek na otevření souboru, ale neprovede jej, protože nedůvěryhodným uživatelům není povoleno otevírat chráněné soubory.

Poznámka: Operační systém obvykle neposuzuje důvěryhodnost programů, ale důvěryhodnost
(a oprávnění) uživatelů, kteří je spustili nebo pod kterými vykonávají svůj kód. Program test.exe
běžící pod administrátorským uživatelským účtem může měnit i obsah důležitých systémových souborů a složek.
Stejnému programu běžícímu s právy běžného uživatele však operační systém měnit systémové soubory a nastavení nedovolí.
Rozhodování o důvěryhodnosti na základě obsahu binárního souboru aplikace a jejího chování provádějí antivirové programy a jiný bezpečnostní software.

Jakmile jádro zpracování požadavku dokončí, vrátí řízení aplikaci a její kód se začne vykonávat opět na úrovni Ring 3. Princip popsaný v předchozích třech odstavcích se nazývá mechanismus systémových volání a detailně se jím budeme zabývat později.

Virtuální paměť

Jedním z úkolů operačního systému je izolovat jednotlivé běžící aplikace od sebe takovým způsobem, aby jedna nemohla (ať úmyslně nebo neúmyslně) škodit druhé, nemá-li k tomu potřebná oprávnění. A aplikace nemůže škodit jiným aplikacím, pokud nedokáže číst a měnit jejich paměť. Aby byla tato podmínka splněna, musí systém každému programu vyhradit oblast paměti, kam nemůže nikdo jiný přistupovat. Jedna z cest, jak toho dosáhnout, vede skrz mechanismus virtuální paměti, který je zabudován do většiny moderních procesorů.

Celý princip stojí na myšlence virtuálních adres. Drtivá většina kódu (tedy i kód jádra operačního systému) nepracuje přímo s fyzickými adresami (adresami používanými pro přímý přístup do operační paměti), ale s adresami virtuálními. Mapování virtuálních adres na fyzické je uloženo ve speciálních datových strukturách – například stránkovacích tabulkách. A s nimi může manipulovat pouze kód běžící v režimu jádra.

Poznámka: S fyzickými adresami pracuje obvykle pouze kód spravující právě datové struktury,
které uchovávají informace o překladu virtuálních adres. Běžným aplikacím operační systém (a ani
procesor) nedovoluje s fyzickými adresami pracovat vůbec.

Vlastní překlad virtuálních adres na fyzické zajišťuje část procesoru známá pod názvem memory management unit (MMU). Protože překlad probíhá na úrovni hardware, je pro aplikaci (a i pro většinu jádra) zcela transparentní. Program si klidně může „myslet“, že pracuje přímo s fyzickou pamětí, protože překládání adres je před ním skryto.

Překlad obvykle není možné definovat pro jednotlivé virtuální adresy, ale pro jejich bloky (například o velikosti 4 KB), které se označují jako stránky.

Použití virtuální paměti poskytuje následující výhody:

  • Ochrana – některé procesory umožňují pro každou stránku určit, jaké operace s ní lze provádět. Tímto způsobem operační systém například chrání některé oblasti fyzické paměti proti zápisu. U modernějších procesorů je též možné zakázat na určitých stránkách spouštění kódu. Navíc ne každý blok fyzické paměti musí být viditelný přes nějakou virtuální stránku. Tím lze zamezit přístupu do oblastí, ve kterých jsou uložena citlivá data.

  • Iluze souvislosti – souvislý blok virtuálních adres je možné namapovat do několika nesouvislých bloků fyzické paměti. Situaci ukazuje obrázek. Souvislý blok virtuální paměti je namapován do dvou od sebe oddělených souvislých bloků fyzické paměti.

Virtualizace paměti

iluze souvislosti

  • Privátní paměť – každá aplikace může mít rezervován svůj rozsah virtuálních adres, který ostatní aplikace neuvidí. Například 32bitové verze Windows vytvoří každému programu paměťový prostor (též virtuální adresový prostor či jen adresový prostor) o velikosti 2 GB (od adresy 0x00000000 do adresy 0x7FFFFFFF). Protože jádro může měnit mapování mezi virtuálními a fyzickými adresami za běhu, nevadí, když více aplikací pracuje se stejnými hodnotami virtuálních adres – když aplikace dostane čas na procesoru, operační systém změní mapování tak, aby se na virtuálních adresách objevovala právě její data.

  • Iluze dostatku paměti – ne každá virtuální adresa musí být namapována na nějakou fyzickou. Operační systém může toto mapování vytvořit až v případě potřeby, která nastává ve chvíli, kdy se z dané adresy pokusí někdo číst, nebo na ni zapisovat. Na 32bitových verzích Windows má tedy aplikace k dispozici paměťový prostor o velikosti 2 GB, jehož valná většina není zpočátku namapována do fyzické paměti. Program může „žít“ v iluzi, že má k dispozici celé 2 GB operační paměti, ačkoliv se na počítači nachází třeba jen 512 MB fyzické paměti RAM. Úkolem operačního systému je tuto iluzi co nejvíce podporovat.

  • Sdílení paměti – na jednu fyzickou adresu lze namapovat více virtuálních adres, každá z nich může povolovat pouze určitý druh přístupů (čtení, zápis, vykonávání kódu). Tato možnost dovoluje uchovávat v paměti pouze jednu kopii dat, která ale může být viditelná ve virtuálním adresovém prostoru několika aplikací, dokonce se může objevit vícekrát v adresovém prostoru jedné aplikace. Sdílená paměť nachází využití zejména při výměně dat mezi adresovými prostory.

Sdílená paměť

sdilena pamet

Adresový prostor procesů A a B sestává ze tří bloků virtuální paměti. Dva z nich jsou do fyzické paměti mapovány tak, aby se nepřekrývaly, takže jsou viditelné pouze z daného adresového prostoru. Stránky třetího bloku jsou však v obou adresových prostorech překládány na stejné fyzické adresy. Oba procesy tak vidí obsah stejné oblasti fyzické paměti a při vhodném nastavení oprávnění stránek může například proces A pozo- rovat změnu obsahu, kterou proces B způsobí tím, že do sdíleného bloku zapíše nějaká data.

Procesy, vlákna, joby

Nyní již víte, že každá aplikace má svůj vlastní kousek virtuální paměti – virtuální adresový prostor –, kde si může „žít“ a díky mechanismu systémových volání komunikovat s okolním světem (například číst stav klávesnice). Pro správné fungování takové aplikace musí operační systém udělat ale mnohem víc, než vytvořit nový virtuální adresový prostor. A aby OS mohl s aplikacemi jako celky lépe pracovat, byly zavedeny pojmy proces, vlákno a job. Každá aplikace je reprezentována jednou entitou, která se nazývá proces. Proces si můžete představit jako kontejner, který sdružuje všechny potřebné informace. Například:

  • Virtuální adresový prostor včetně seznamu rezervovaných a alokovaných bloků virtuální paměti.

  • Jedinečný identifikátor procesu. Ve Windows se jedná o číslo PID (process identifier). V každém okamžiku mají všechny běžící procesy v systému různá čísla PID.

  • Seznam oprávnění, kterými aplikace disponuje. Tato informace je sdružena do struktury (objektu) tokenu.

  • Entity zodpovědné za vykonávání kódu procesu. Nazývají se vlákna.

  • Seznam objektů, se kterými proces právě pracuje. Jedná se například o otevřené soubory, klíče registru či bloky sdílené paměti. Tento seznam neobsahuje přímé odkazy na jednotlivé objekty (jejich adresy v paměti jádra), ale tzv. handle. Handle k jednotlivým objektům vydává součást jádra, která se nazývá správce objektů. Tento mechanismus nepřímých odkazů dovoluje implementovat například zabezpečení a šetří paměť.

Vlastní vykonávání kódu tedy není úkolem procesu, ale jeho vláken. Součást jádra označovaná plánovač úloh zajišťuje, aby se jednotlivá vlákna na procesoru střídala, a tím vznikal dojem současného běhu více aplikací, než má počítač k dispozici procesorů.

Od Windows 95 probíhá střídání vláken na procesoru preemptivně – vlákna proces střídání na procesoru sice ovlivnit mohou, ale nemají nad ním plnou kontrolu. Plánovač každému z nich přidělí na krátký čas procesor a po jeho uplynutí jej přidělí dalšímu v pořadí. Tento proces se také označuje jako plánování a probíhá transparentně vzhledem k vláknům. Každé si může „myslet“, že procesor patří jenom jemu a není přidělován jiným entitám.

To je velký rozdíl oproti konceptu kooperativního plánování, který využívaly systémy Windows starší než Windows 95. V tomto modelu je procesor vláknu odebrán pouze na jeho explicitní žádost. Pokud se tedy nějaká aplikace dostane do nekonečné smyčky, může způsobit zatuhnutí celého systému, protože její vlákno nikdy nepožádá o odejmutí procesoru.

Stejně jako proces, i vlákno patří mezi velmi složité struktury a zahrnuje v sobě mnoho údajů, mezi které například patří:

  • Hodnoty registrů procesoru od okamžiku posledního přeplánování. Ty plánovač načte do registrů, jakmile vláknu přidělí další časový úsek (time slice). Tato struktura nese název CONTEXT a její obsah je závislý na typu procesoru.

  • Jedinečný identifikátor vlákna – TID (thread identifier).

  • Oprávnění reprezentovaná opět objektem tokenu. Většina vláken touto strukturou nedisponuje, a tak seznam oprávnění dědí z tokenu jejich procesu. Windows tak umožňuje, aby různá vlákna jednoho procesu disponovala různými sadami oprávnění.

  • Prioritu, která udává, jak často by měl plánovač pouštět dané vlákno na procesor.

Krom jednotlivých procesů a jejich vláken lze ve Windows pracovat i se skupinami několika procesů jako s nedělitelnými entitami. Tato vlastnost nachází užitek, pokud několik procesů spolupracuje na splnění jedné úlohy. Každou takovou skupinu procesů popisuje jeden objekt job a díky němu lze celé skupině nastavit například limity na využití systémových prostředků.

Knihovny DLL a rozhraní Windows API

Kód programů je uložen v tzv. spustitelných souborech na pevném disku či jiném médiu. Aplikace nativně psané pro Windows jsou uloženy v souborech ve formátu PE (Portable Executable). Tyto soubory operační systém při vytváření nového procesu načte (namapuje) do nového virtuálního adresového prostoru. Jakmile má nový proces svůj kód v paměti, může jej začít vykonávat.

Soubory formátu PE lze rozdělit na několik druhů:

  • Soubory EXE (přípona .exe) – obsahují vlastní kód procesu. Při vytváření procesu se mapují jako první do jeho paměťového prostoru.

  • Knihovny DLL (přípona .dll) – poskytují aplikacím různé užitečné funkce, například pro práci s prvky grafického uživatelského rozhraní či pro komunikaci po síti. Aplikace mohou příslušnou knihovnu DLL namapovat do svého paměťového prostoru a pak volat rutiny, které knihovna poskytuje. Jakmile program již jejích služeb nepotřebuje dále využívat, může příslušnou knihovnu DLL uvolnit z paměti. Knihovny lze tedy do paměti načítat dynamicky – odtud také pochází zkratka DLL – dynamic link library.

  • Soubory SYS (přípona .sys) – obsahují kód ovladačů jádra.

    Poznámka: O typu spustitelného souboru nerozhoduje jeho přípona, ale vnitřní formát.
    Je například možné vytvořit proces, jehož kód obsahuje soubor ukazka.dll či ovladač s příponou .dat.
    Například aplikace Adobe Reader v minulosti realizovala své pluginy pomocí knihoven DLL, jejichž jména ale měla příponu .api.
    Přípony uvedené v předchozích odrážkách nemusí být striktně dodržovány, jedná se pouze o nepsanou konvenci.

Spuštění nové aplikace probíhá tak, že operační systém vytvoří nový proces, do jehož adresového prostoru namapuje soubor EXE. Následně se systém podívá, jaké knihovny DLL aplikace ke svému běhu potřebuje a také je namapuje. Během mapování nového souboru formátu PE se vždy zkontroluje, jestli jsou v paměti již načteny všechny knihovny DLL potřebné pro jeho běh, a pokud tomu tak není, systém se je pokusí najít a načíst. Jakmile jsou všechny potřebné soubory načteny v paměti nového procesu, systém spustí nové vlákno, které začne vykonávat kód obsažený v souboru EXE.

Knihovny DLL dávají svému okolí k dispozici (exportují) mnoho funkcí. Mezi nejdůležitější knihovny patří kernel32.dll , user32.dll a gdi32.dll. Kernel32.dll exportuje rutiny pro práci s procesy, vlákny, soubory, registry a mnoho dalších funkcí pro komunikaci s jádrem operačního systému. User32.dll sdružuje rutiny obsluhující prvky grafického uživatelského rozhraní, jako okna, tlačítka, menu a textová pole. Gdi32.dll obsahuje funkce pro práci s ikonami a pro kreslení. Téměř všechny rutiny poskytované těmito knihovnami jsou plně zdokumentovány. Protože se málokterá aplikace objede alespoň bez dvou z výše jmenovaných knihoven, tyto funkce se souhrnně označují jako rozhraní Windows API.

Další důležitou knihovnou DLL je soubor ntdll.dll, jehož úkolem je implementovat tu část mechanismu systémových volání, která musí být umístěna v uživatelském režimu. Knihovna plní úlohu jakéhosi překladatele. Když jiné knihovny (například kernel32.dll) potřebují zavolat jádro, obrátí se se svým požadavkem na ntdll.dll, která jej přeloží do řeči, jíž jádro rozumí. Ostatní knihovny DLL tedy mohou fungovat nezávisle na konkrétní implementaci mechanismu systémových volání.

Knihovna ntdll.dll exportuje mnoho funkcí, jež jsou svoji povahou nízkoúrovňové a jen pár z nich je oficiálně zdokumentováno. Ujal se pro ně název nativní API funkce, který bude používán i dále.

Služby a ovladače

Windows umožňuje vytvářet speciální druh aplikací, jejichž primárním úkolem je vykonávat důležité úkoly na pozadí, například defragmentaci souborového systému či zálohování. Tyto entity se označují jako služby (na unixových systémech démoni) a krom absence jakéhokoliv grafického uživatelského rozhraní se od běžných aplikací liší také tím, že jejich běh obvykle neskončí při odhlášení uživatele.

Poznámka: Teoreticky i služby mohou pracovat s prvky grafického uživatelského rozhraní, ale takové chování se silně nedoporučuje.
Protože plní nejrůznější systémové úkoly, služby často disponují vysokým oprávněním, dokonce vyšším než administrátor.
Absence uživatelského rozhraní činí takový program méně zranitelný proti útokům jiných aplikací.

Služby spravuje součást systému s názvem správce služeb (Service Control Manager – SCM).

Ovladače jsou jsou spustitelné soubory formátu PE, jejichž kód je určen k vykonávání v režimu jádra. Mohou provádět úkoly, na které normální aplikace běžící v uživatelském režimu procesoru nemají oprávnění. Jedná se například o přímou komunikaci s periferními zařízeními počítače. Windows interně zacházejí s ovladači jako se speciálním typem služeb. O spouštění a instalaci ovladačů se opět stará správce služeb.