Operační systémy
-=-=-=-=-=-=-=-=-=
I still maintain the point that designing a monolithic kernel in
1991 is a fundamental error. Be thankful you are not my student. You
would not get a high grade for such a design :-)
-- Andrew Tanenbaum to Linus Torvalds
Tak toto by měl být poněkud delší seriál (možná i dvojdílny)
o tom, jak vypadají operační systémy uvnitř. Začneme pěkně
od začátku - od jádra.
Jádro obsahuje věci, které nemůžou dělat normální procesy -
zažizuje pro ně jednotné prostředí, stará se o přepínání procesů,
přístupová práva, jejich komunikaci apod. Existují dva základní
přístupy - mikrojádro a makrojádro.
Mikrojádro a Makrojádro
Makrojádro je jádro, které umí nejenom nejzákladnější věci,
ale i obsahuje ovladače od různých zařízení, jako jsou disky apod.,
stará se o filesystém, síť a komunikaci mezi procesy. Takže proces
potom může říct jadru jednodušše "otevři mi soubor" a nemusí se
o víc starat. Mezi klasická makrojádra patří jádro UNIXu. Jeho free
implementace jsou Linux a FreeBSD
Ne vždycky ale bylo jádro UNIXu jednotné. V prvních verzích
systému bylo rozděleno na dvě části - mikrojádro obsahující
nejdůležitější hardwarově závislé věci (jako je správa paměti).
To bylo napsáno v assembleru ale bylo dostatečně malé na to, aby se
snadno přepsalo na novou platformu. Jeho služeb využívalo vlastní
jádro, které už bylo v C a tak bylo nezávislé na architektuře.
Z důvodů efektivity se od toho později upustilo.
Přesto ale vývoj mikrojader šel dál. Mezi klasická mikrojádra
patří Mach (free implementace je GNU Mach a další). To umí správu
paměti, procesů a komunikaci mezi nimi pomocí zpráv. O ovladače
zařízení se starají speciální procesy. To odstraňuje základní
problém UNIXu - tedy to, že přidání jakéhokoliv ovladače znamená
rekompilaci jádra. Vzniklé jádro je sice velmi efektivní a šité
na míru hardwaru, ale rekompilace trvá dlouho a proto je nepříjemná.
Na mikrojádru Mach bylo potom postaveno mnoho operačních
systému. Mezi nejznámější patří NeXtStep. Ale třeba i Linux byl
předělán tak, aby pod Machem pracoval a jmenuje se MkLinux. Pro
vývojáře OS to má velkou výhodu - jejich OS rázem běží všude tak,
kde běží Mach a ten běží téměř všude. Navíc je možné spustit více
operačních systémů najednou apod. Mimochodem tuto architekturu mají
i WindowsNT ale založenou na jiném mikrojádře. Nevýhoda tohoto
návrhu je efektivnost. MkLinux je opravdu několikanásobně pomalejší,
než klasický Linux a to samé platí samozřejmě i o ostatních.
Myšlenku mikrojádra lze ale dotáhnou dál. Existují systémy,
které prostě zrušily makrojádro a jeho práci rozdělili mezi
specializované servery a knihovny. Jeden server je ovladač
harddisku. S ním komunikuje server pro partitiony, na který je
napojen ovladač filesystému. Pokud program chce otevvřít soubor,
řekne to knihovně, ta se spojí přímo s daným serverem a zařídí to.
Další servery se mohou starat o zavádění programů, autentifikaci,
emulaci API jiného OS, nebo síť. Tyto systémy jsou velmi zajímavé.
Mají totiž mnoho skvělých možností - jsou snadno distribuovatelné
(jeden OS běží na více počítačích, kde každý nabízí nějaké
prostředky. Úlohy se potom automaticky distribuují mezi jednotlivé
počítače tak aby běžely co nejlrychleji.) Jejich vývoj je úžasně
jednoduchý - věci v jádře se obvykle vyvíjejí těžko (pro každý test
je nutné rebootovat, chyba většinou způsobí pád systému apod).
Nežto v těchto systémech se jedná o vývoj normálního programu.
Můžete tedy například mít spuštěnou starou verzi pro práci
a přitom testovat vývojovou verzi, ani meusíte být supervisorem,
protože každý uživatel takové progrmy může ladit apod. Jádro
klasicého OS musí být co nejmenší, protože se jedná o část paměti,
která je navěky zablokovaná - není ji většinou možné ani odswapovat
na disk. V mikrojádrových OS tento problém mizí. Navíc je možné
například zneužívat ovladačů od filesystému tak, že si vytvoříte
ftp filesystém pro stahování, nebo zip filesystém pro prohlížení
zabalených souborů. Hlavní nevýhodou těchto OS je rychlost. Jsou
ze všech zmíněných nejpomalejší. Některé další potíže vysvětlím
později. Tyto OS se často také nazývají objektově orientované
systémy. Ne snad proto, že by byly psané v C++, ale kvůli tomu,
že na jednotlivé servery lze nahlížet jako na objekty. Free
implementace tového OS je například Hurd, nebo Vsta. Nejzajímavější
komerční implementace je QNX, na kterém beží například Česká
televize. Ten ale není úplně klasickým objektově orientovaným OS,
protože pochází z doby, kdy se takové OS nedělaly. Firma Sun zase
vyvíjí OS jménem Spring.
Procesy a thready
Podle klasického pojetí proces je samostatný program běžicí
pod OS. Každý proces může mít více threadů. Každý s threadu běží
samostatně v multitaskingu a sdílí pamět. Procesy mezi sebou paměť
nesdílí. Klasický UNIX uměl pouze procesy. Každý proces má svůj PID
(process identification), přes který se dá na něj odvolávat. Vzniká
tak, že jeho otec zavolá službu fork, která rozdělí proces na dva
identické. Proto každý proces má svého otce a procesy jsou tedy
uspořádáný do stromové struktury. Kořenem této struktury je proces
init, jehož otcem je jádro a má PID 1.
Později SystemV přidal sdílenou paměť a tvrdilo se, že thready
nejsou třeba. Ale nebyla to tak úplně pravda a tak se ve standardu
POSIX oběvila definice API pro thready.
V UNIXu přinesly thready mnoho potíží - bylo nutné přepsat
knihovny. Napsat program tak, aby byl "thread safe" vůbec není
jednoduché. Klasický přiklad je proměnná errno. Ta ale v threadech
proměná být nemůže, protože v případě, že ve dvou threadech
by naráz vznikla chyba, jedna z chybových hodnot by byla nenávratně
ztracena a jeden z threadů by dostal špatnou hodnotu a tak by se
mohl zachovat úplně špatně. Jiný příklad je knihovna stdio. Je
velmi složité ošetřit to, aby dva procesy zároveň nepřistupovali
k jednomu souboru a nevznikl tak binec ve struktuře FILE. K tomu je
nutné thready synchronizaovat - na to slouží semafory (každý thread
při přístupu k souboru strukturu FILE semaforem uzavře a ostatní
tak počkají, dokud thread není hotov. Potom se probudí. Jednodušší
provedení semaforů jsou spin locky. Ty se používají hlavně
na víceprocesorových platformách. Program prostě čeká v nekonečné
smyčce dokus se určitý byte v paměti neoznačí na "otevřeno", pak ho
atomicky uzamkne. Spinlocky jsou mnohem rychlejší, než semafory,
ale v případě, že proces čeká, zabírá procesor a proto se hodí
jen na zamykání v místech, kde se nepředpokládá kolize a kde se
čeká velmi krátkou dobu. Další prostředek jsou condition proměné.
Pokud thread čeká na to, až se nějaké proměná na něco nastaví, může
usnout a procesy, co proměnou změní ho pak probudí. Problém ale zase
je v tom, že mezí tím, než proces ošahá proměnou a usne jiný thread
tu proměnou může změnit, ale právě usínající thread se už nezbudí,
protože ještě nestihl usnout. Proto se k tomu musí ještě přidat
semafory a už to vůbec není jednoduché.
Proto bylo nutné knihovny docela dost přepsat a tak například
v Linuxu thread safe knihovna (glibc2) je žhavá novinka a já ji
nainstaloval minulý týden.
V UNIXu jdou thready implementovat dvěma způsoby - první je tzv
user space. Tam si proces pomocí časovače zařizuje přepínaní sám.
To má výhodu, že různé čekání na semafory apod. je velmi rychlé,
protože to zařizuje knihovna a nemusí se kvůli tomu volat jádro.
Na druhou stranu je možné dodělat podporu threadu do jádra. To
sice zpomaluje vytváření a řízení threadů ale na druhou stranu to
umožňuje, aby taždý thread bežel na jiném procesoru a tak se používá
častěji.
V nových systémech je ale tendence rozdíl mezi thready a procesy
schovat. Přistupovat k oběma stejně. Při vznikání procesu se potom
udává, jestli proces má sdílet, nebo kopírovat pamět (a další
zdroje, jako otevřené soubory apod.) od svého otce. To dává lepší
možnost pořádně doladit, co se má vlastně stát. První OS co toto
implementoval byl Plan9. Dnes to používá například i Linux.
Základní služby jádra
Mezi věci, které každé jádro musí zařídit paří:
- Scheduling procesů
- Memory management
- Komunikaci mezi procesy
- Podporu pro realtime
- Přístup k hardwaru
Makrojádra navíc zařizují věci jako:
- Filesystém
- Cache
- Síťe a další
- HH -
hubicka@freesoft.cz
výheň