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ň