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ň