Archiv rubriky: Jak se stát ajťákem

Míra abstrakce

Z minulých dílů už víte, že programovací jazyk není nic jiného, než určitým domluveným kódem psaný text, který se pak skrz kompilátor převádí do strojového kódu.

Jedno důležité členění programovacích jazyků, které je dle mého důležité pochopit, je míra abstrakce.

Nízká nebo žádná míra abstrakce

Programovací jazyk, který má nízkou nebo téměř žádnou míru abstrakce je vlastně jen převlečené zádávání instrukcí pro procesor ale v trochu přehlednější formě, než po jednotlivých bitech.

Níže následuje ukázka (zdroj: Wikipedie) pro srovnání.

  • Vlevo je strojový kód funkce pro výpočet Fibonacciho čísla v x86 instrukční sadě. Pro zkrácení místo 32-bitového zápisu instrukce 10001011010101000010010000001000 jsou jednotlivé instrukce uvedeny v hexadecimální formě.
  • Vpravo je zápis té samé funkce v „assembly“ v x86. Assembly se nepovažuje úplně za konkrétní programovací jazyk, je to spíš čitelnější forma zápisu svázaná s konkrétní instrukční sadou. Tzn. assembly pro x86 se bude lišit od assembly pro ARM64.

Strojový kód (x86, výpočet Fibonacciho čísla)

8B542408
83FA0077
06B80000
0000C383
FA027706
B8010000
00C353BB
01000000
B9010000
008D0419
83FA0376
078BD989
C14AEBF1
5BC3

Assembly (x86, výpočet Fibonacciho čísla)

_fibonacci:
        movl $1, %eax
        xorl %ebx, %ebx
.fibonacci_loop:
        cmpl $1, %edi
        jbe .fibonacci_done
        movl %eax, %ecx
        addl %ebx, %eax
        movl %ecx, %ebx
        subl $1, %edi
        jmp .fibonacci_loop
.fibonacci_done:
        ret

Psaní v assembly vyžaduje, že se vyznáte v instrukční sadě konkrétního čipu/procesoru, nad kterým programujete. To abyste se nezbláznili ze psaní strojového kódu napřímo.

Tak se místo toho zblázníte v assembly.

Na příkladu tohoto jednoduchého prográmku to zas tak složité není. x86 procesory obsahují různé „registry“ mezi kterými přesouváte bity (movl) a provádíte nad nimi bitové operace (xorl, addl, subl) a zároveň skáčete mezi instrukcemi (jbe, jmp, ret). Jak konkrétně se v x86 pracuje je mimo rozsah tohoto návodu.

Teoreticky je to možné napsat v assembly komplikovanější software, ve kterém zobrazujete nějaké grafické prvky, čekáte na vstup z klávesnice/myši a podobně, prakticky je ale téměř nereálné to rozchodit v rozumném čase.

Další nevýhodou psaní kódu s nízkou mírou abstrakce je fakt, že váš kód je svázán nejenom s konkrétní instrukční sadou ale i s konkrétní verzí instrukční sady a někdy i s konkrétním modelem procesoru.

Výhody a nevýhody nízkoúrovňového programování

Výhody:

  • máte možnost využít naplno fyzické možnosti konkrétního čipu nebo procesoru. Váš software může být plně optimalizovaný a extrémně rychlý.

Nevýhody:

  • je to šíleně složité a pracné, komplexnější software je prakticky nemožné napsat v rozumném čase.
  • váš software poběží jen na konkrétní instrukční sadě, na konkrétním typu procesoru ale i na konkrétním modelu čipu/procesoru. To nevadí, pokud programujete software pro řadu nějakých robotů se stejným čipem, ale o tomto typu programování tento návod není.

Vysoká míra abstrakce

„Vysokoúrovňové“ programovací jazyky umožňují psát v kodu, který je blíže k přirozené řeči a zbavují nás závislosti na konkrétní instrukční sadě. Už nás nezajímají konkrétní instrukce pro procesor, protože kód, který napíšeme, přeloží do strojového kódu kompilátor.

Níže je ukázka funkce pro výpočet Fibonacciho čísla v programovacím jazyce C.

int fibonacci(int n)
{
    if (n <= 1)
        return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

Všimněte si, že ten program je podstatně kratší! Zabírá pouze 6 řádků textu zatímco v Assembly ten samý kód zabírá 14 řádků textu. Navíc v něm vidíte anglická slova jako „if“ nebo „return„. Zároveň se zde vyskytuje „int“ a z minulých kapitol byste už měli vědět, že „int“ reprezentuje celé 32 bitové číslo. (pozn.: v tuto chvíli není třeba kódu rozumět)

Tento kód se ale musí do strojového kódu zkompilovat pomocí kompilátoru. Přestože kompilátory jsou jedny z nejsložitějších programů, které na světě existují, tak nikdy nedokážou žádnou aplikaci zkompilovat opravdu optimálně (snad jen kromě velmi jednoduchých, ukázkových programů, jako ten výše).

V důsledku toho lze říct, že jakýkoliv software, který se postaví skrz zkompilovaný programovací jazyk, téměř nikdy nevyužívá 100% možnosti výkonu procesoru pro úlohu, kterou chce vykonat.

Ono to ve skutečnosti nevadí. Procesory jsou brutálně rychlé a moderní kompilátory jsou velmi dobře udělané. Zkušeného „běžného“ ajťáka vůbec nezajímá, jak vypadá strojový kód vygenerovaný kompilátorem. Celá pointa programovacího jazyka je, že chceme vyrábět software v kódu, který je mnohem přehlednější, srozumitelnější a intuitivnější, než psaní v assembly/strojovém kódu. V kódu, se kterým je jednodušší psát mnohem komplexnější software.

Výhody a nevýhody vysokoúrovňového programování

Výhody

  • V programovacím jazyce je možné napsat mnohem komplexnější software
  • Kompilátory podporují celou řadu instrukčních sad. Náš software je možné provozovat na více zařízeních.

Výhody

  • V programovacím jazyce je možné napsat mnohem komplexnější software
  • Kompilátory podporují celou řadu instrukčních sad. Náš software je možné provozovat na více zařízeních.

Shrnutí

  • Programovací jazyky se podle míry abstrakce dělí na nízkoúrovňové a vysokoúrovňové
  • Nízkoúrovňové jazyky jsou jen o něco čitelnější způsob, jak zapisovat instrukce přímo ve strojovém kódu.
  • Vysokoúrovňové jazyky jsou na strojovém kódu nezávislé. Jsou konstruovány hlavně tak, aby byly přehledné, intuitivní a aby se v nich dal psát komplexnější software.

Programovací jazyk

Z předchozích dílů už víte, že nejdůležitější součást počítače je procesor který neustále přijímá instrukce, co má dělat. Instrukce v moderních procesorech má 64 bitů a vy už víte, že to je ve skutečnosti 64 nějakých obvodů, kterými putuje elektřina.

Takže co když chcete napsat nějaký program? Třeba počítačovou hru, nějakou svojí vlastní websajtu, eshop, nebo cokoliv jiného?

Teoreticky si můžete vzít k ruce manuál konkrétní instrukční sady a svojí aplikaci napsat skrz jedničky a nuly do procesoru napřímo.

x86-64 má přes 2500 stránek, kompletní dokumentace k ARM64 dokonce přes 8000.

Hodně štěstí!

Můj první 64-bitový program!
0001100000000000000000010000000000000000000000000000000000000000
0001000000000000000000000000000000000000100000000000000000000010
1100000000000000000000000000000000000000100000000000000000000010
….
Z tohohle se dřív nebo později zblázníte. To, že je něco teoreticky možné neznamená, že byste to měli dělat.

Jak se teda programuje? Jakým způsobem ajťáci píšou software?

Místo toho, abyste pracovali s jedničkami a nulami tak si vymyslíte nějaký „kód“ se kterým se pracuje jednoduššeji.

Programovací jazyk není nic jiného, než pro člověka čitelnější instrukce pro procesor. Procesoru je to jedno, ten pracuje se 64 bity ale to je pro člověka s dnešní složitostí procesorů prakticky nereálné.

Strojový kód
(vymyšlené pro ukázku)

010010101001000100
100010010100001011
111110000101000011

Programovací jazyk
(vymyšlené pro ukázku)

KDYŽ Stisknutá klávesa
JE „Enter“
TAK ulož data do souboru

Kompilátor a strojový kód

Jakýkoliv programovací jazyk se píše jako lidsky čitelný text v nějakém kódu, který je mnohem přehlednější a srozumitelnější, než se orientovat v jedničkách a nulách.

Tento text ale sám o sobě nejsou instrukce pro procesor. Je to pouze text v nějaké znakové sadě.

Kompilátor (nebo compiler) je program, který převádí text do strojového kódu. Strojový kód je jen takovej chytřejší název pro instrukce pro procesor.

Co je vlastně kompilátor

A to je vše. Tadá. Právě jste se naučili podstatu programovacích jazyků. Není to nic jiného, než nějaký „kód“ který je pro člověka čitelnější než přímá práce s jedničkami a nulami. Tento kód se zapisuje jako text ale aby jej bylo možné spustit, je nutné tento text převést do strojového kódu skrz kompilátor.

Úžasná, moderní a symetrická grafika, jak se z programovacího jazyka stanou instrukce pro procesor.

Kompilátor je taky jenom program.

Takže vás možná napadla vtipná otázka: kdo zkompiloval kompilátor?

K tomu mohlo dojít dvěma způsoby:

  • Kompilátor se napsal v již existujícím programovacím jazyce.
  • Kompilátor někdo napsal přímo ve strojovém kódu procesoru.

    Psaní v nějakém programovacím jazyce je vždy milionkrát produktivnější, než psaní instrukcí přímo pro procesor a proto napsat program přímo přes instrukce je opravdu šílená práce na dlouho. Jakmile však máte kompilátor hotový, můžete začít rovnou psát aplikaci ve svém novém programovacím jazyce.

    Celý tento proces můžete zkombinovat. Nejdřív skrz instrukce pro procesor napíšete kompilátor pro velmi jednoduchý programovací jazyk. V tomto jednoduchém jazyce poté napíšete mnohem jednoduššeji kompilátor pro ještě lepší programovací jazyk. Tomuto procesu se říká bootstrapping a mně osobně přijde fascinující, jak lze procesor využít pro napsaní programu, který vám umožní napsat podstatně lepší a bohatší programy.

    Jeden z prvních kompilátorů vznikl v roce 1957 a psal se 18 let!

Shrnutí

  • Programovací jazyk je kód, který umožňuje lidštější psaní softwaru jako textu
  • Kompilátor (compiler) je program, který převede kód napsaný programovacím jazykem do strojového kódu
  • Strojový kód je název pro spustitelný program což je jen sada instrukcí pro procesor
  • Bootstrapping je jeden ze způsobů, jakým vznikají nové programovací jazyky. Nejdřív se napíše kompilátor v instrukcích pro procesor pro velmi základní prvky programovacího jazyka a pak se díky tomuto kompilátoru, už v daném jazyce, napíše rozšířenější kompilátor.

Terminál, konzole, příkazová řádka

Telegraf bylo první elektronické nebo rádiové zařízení pro komunikaci z bodu A do bodu B, vymyšlené už v polovině 19. století.

Na telegraf navázalo zařízení, kterému se v angličtině říká teleprinter nebo telex, v češtině dálnopis. Zařízení na dálnopis fungovalo tak, že člověk ťukal něco do klávesnice a tento text se rovnou tisknul na papír na straně příjemce.

Teleprinter, nebo telex, zařízení na dálnopis. Zdroj: Wikipedia

Nejstarší počítače se někdy po 2. sv. ovládaly počítače přes různá táhla, páky, spínače a podobně. Příkazy se později do počítačů zadávaly přes děrkovací štítky a s nástupem dálnopisu (teleprinetrů) vznikly první zařízení, kde uživatel interagoval s počítačem skrz klávesnici.

Veškerá interakce s počítačem se tiskla na papír. Zadali jste příkaz, stiskl „Enter“, příkaz se spustil a veškerý výstup, který příkaz vygeneroval, se tisknul na roli papíru. Abyste zjistili, jestli se příkaz provedl a co vlastně udělal, museli jste se podívat na papír co se vytisklo.

Až později vznikly obrazovky a s nimi zaniknul i neustálý tisk na tehdejších hlasitých jehličkových tiskárnách.

Zdroj: Wikipedia

Terminál je název pro fyzické zařízení, pomocí kterého se zadávají příkazy do nějakého jiného, odděleného zařízení někde jinde. Slovo konzole vzniklo až později pro pojmenování počítačů s obrazovkami. Ty technicky vzato mohly fungovat i jako terminály.

Rozdíl mezi těmito pojmy v IT už kompletně zanikl. Konzole, terminál a příkazový řádek je jedno a totéž. Slovo „terminál“ se používá už jen v Linuxech ale při práci s Linuxy většinou nemluvíte o terminálech ale o shellu nebo o bashi, což vysvětlím později.

Konzole nebo „příkazová řádka“ dnes

Slovo „terminál“ se už moc nepoužívá, většina ajťáku mluví buď o konzoli, o „shellu“ (co je „shell“ popíšu v pozdější kapitole) nebo o „command lajně“ z anglického „command line“, takto se označuje nejčastěji příkazová řádka ve Windowsech.

Dvě příkazové řádky: jedna běžící přímo ve Windowsech, druhá běžící ve virtualizovaných Linuxech

Operační systémy pro koncové uživatele už dávno příkazové řádky odhodily stranou. Obyčejní uživatelé používají myš a klávesnici pro interakci s grafickými prvky, které reprezentují graficky ovladatelné aplikace. Běžní uživatelé již nepotřebují znát ručně zadávané příkazy.

Příkazová řádka nezmizela ale stal se z ní samostatný program. Pro moderního ajťáka je často normální používat více druhů příkazových řádek, které se liší podle OS kde se používají ale i podle podporovaného skriptovacího jazyka.

Příkazové řádky v dnešní době, přestože už to není potřeba, přebírají stylizaci starých CRT obrazovek kde se zobrazoval světlý text na černém pozadí. Pouze Apple to dělá jinak – na Mac počítačích se ve výchozím stavu zobrazuje příkazová řádka s černým textem na bílém pozadí.

Výchozí podoba příkazové řádky v Mac počítačích

Ajťáci velmi často s příkazovou řádkou pracují a nebo vytváří jednoduché aplikace, které nemají žádnou grafickou reprezentaci a výstup těchto aplikací si lze prohlédnout jen do příkazové řádky. Vytvářet grafické reprezentace je totiž dost pracné a pro jednoduché aplikace stačí pouze textový výstup.

Shrnutí

  • Pojmy terminál a konzole se v dnešní době už nerozlišují. Tyto pojmy pochází historicky ze zařízení, přes které se posílaly informace nebo zadávaly instrukce pro počítače.
  • Operační systémy historicky nebyly nic jiného, než příkazové řádky, kam bylo možné zadávat jména programů, které se měly spustit.
  • V dnešní době jsou příkazové řádky samostatné programy v mnohem komplexnějších operačních systémech.

Použité zdroje

Virtuální souborový systém

Vzhledem k tomu, že souborových systémů existuje celá řada tak operační systémy implementují virtuální souborový systém nebo VFS podle anglického „virtual filesystem“.

Můžete mít disk rozdělený na partitiony kde každá partitiona může mít svůj vlastní souborový systém — v rámci OS však budete pracovat se soubory úplně stejně na obou partitionách. Díky OS vás vůbec nezajímají složitosti jednotlivých souborových systémů.

Jak se v OS zobrazují jednotlivé partitiony

Většina ajťáků tak při programování pracuje se soubory úplně stejně, jako běžný uživatel, vždy v kontextu konkrétního OS který soubory implementuje svým vlastním způsobem.

Vrstva, na které většina ajťáků pracuje se soubory.

OS díky VFS skrývá složitosti souborových systémů a mají svůj vlastní způsob adresování.

Windows

Windowsy každý VFS označují velkým písmenem podle abecedy. Z historických důvodů se první dostupný prostor označuje jako C, každý další prostor jako D, E, F…

Tento prostor je taky kořenová složka – anglicky root directory – do které lze ukládat další složky a soubory. Pro prostor C je to adresa C:\

Adresy se ve Windowsech píšou s „obráceným lomítkem“ – „\“. Každá část mezi obráceným lomítkem je složka, která může obsahovat soubory. Poslední název za lomítkem je buď složka, nebo soubor, například C:\slozka\slozka2\abc.

Ukázka adres a souborů ve Windowsech. Adresa složky je „C:\Windows\System32\drivers\etc“ a v ní jsou soubory jako „hosts“, „lmhosts.sam“ atd.

Nutno říct, že Windows žádné jiné FS kromě FAT32 a NTFS nepodporuje.

Linux

Linuxový souborový systém je mnohem bohatší protože nezahrnuje pouze složky a soubory (a jejich související funkcionality) jako Windows.

V Linuxech má kořenová složka adresu / (ano, pouze lomítko).

V této kořenové složce jsou zpravidla další složky, které mají víceméně stejné nebo aspoň podobné uspořádání napříč různými Linuxovými distribucemi.

Obsah kořenové složky v Linuxové distribuci Ubuntu

V Linuxech se konkrétní partitiony musí do souborového systému „namountovat“ což je proces, ve kterém se nad konkrétní partitionou vytvoří v souborovém systému konkrétní složka, která může být v souborovém systému kdekoliv.

Cesty v Linuxech mají podobu /nejaka/cesta/nekam – oproti Windowsům (obrácené lomítko) se používá obyčejné lomítko. Další podstatný rozdíl je, že daná cesta nemusí reprezentovat pouze složky nebo soubory ale i datové toky (o tom si povíme někdy jindy, to teď není podstatné).

Shrnutí

  • Virtuální souborový systém, nebo VFS, je abstrakce, kterou OS skrývá složitosti a rozdíly mezi jednotlivými souborovými systémy
  • Ajťáci většinou pracují na úrovni souborů ve VFS. To znamená na úrovni souborů a adres se kterými pracují i běžní uživatelé.

Souborový systém

V předchozích dílech jsem pojmenoval, co je to souborový systém a partitioning. Teď je na čase si o souborových systémech něco říct.

Souborové systémy mají často v názvu fs z anglického „file system“.

O moderních souborových systémech by průměrnémy ajťák měl vědět:

  • Pokud máte nějakou flasku, pak je na ní nejspíš FAT32
  • Pokud máte na PC Windows, je to pravděpodobně NTFS
  • Pokud máte v ruce Androida, je to pravděpodobně ext4
  • Pokud pracujete s čímkoliv od Applu, je to pravděpodobně APFS
  • Pokud pracujete s Linuxem,nejčastěji se však setkáte s ext4, ale pak i s btrfs, XFS, ZFS a nejspíš i dalšími…

A to je vše.

To je vše?

Ano.

Souborové systémy nejsou jen adresy a soubory jako například „C:\slozka\soubor.txt“. Souborové systémy podporují mnohem víc konceptů jako jsou oprávnění, metadata, historii, šifrování, linkování atd. apod.

Někteří profesionální ajťáci v životě zkusili se souborovým systémem pracovat napřímo a od té doby varují ostatní, aby se do žádné takové bláznivé akce nepouštěli.

Běžný ajťák nikdy nepracuje se souborovým systémem napřímo. Viz. další kapitola.

Shrnutí

  • Mezi nejznámější a nejpoužívanější souborové systémy patří FAT32, NTFS a ext4
  • Běžný ajťák však nikdy nepracuje se souborovým systémem napřímo

Partitioning

V minulém díle jsem pojmenoval, jak souborové systémy oddělují disk na adresnou část a na datovou část, která obsahuje soubory.

Důležitá pointa: souborový systém je pouze vymyšlený standard, vymyšlená vrstva, jakým způsobem lze organizovat bity na libovolném uložišti. Není to nic fyzického.

Otázka za 2 bludišťáky: Je možné mít na jednom fyzickém zařízení víc souborových systémů?

Odpověď zní: Ano! Každý souborový systém musí mít v uložišti přesně vymezený rozsah. Tomuto přesně vymezenému rozsahu se říká partition a proces dělení uložiště na jednotlivé partitiony se říká partitioning.

Příklad, jak může být na fyzickém uložišti zorganizováno více souborových systémů.

Partitioning se do češtiny překládá jako rozdělení, partition jako oddíl — nikdo normální to tak v češtině nenazývá, místo toho uslyšíte počeštěné „pártyšna“, „rozpártyšnování“, „pártyšning“.

Jenže teď máme ten samý problém, jako v minulém díle: kam uložíte údaj o tom, kde který souborový systém začíná a kde končí?

Partition table

Odpověď je úplně stejná. Musíte si nejdřív vymyslet nějaký standard, ve kterém říkáte, jak si dané uložiště rozdělíte na jednotlivé souborové systémy – v tomto případě se tomu říká partition table.

Těchto standardů je také víc, v počítačích jsou populární tyto dva:

  • MBR (Master Boot Record) = starší standard (1983)
  • GPT (GUID Partition Table) = novější standard (1990)

V těchto standardech je disk rozdělen na dvě části stejně jako souborový systém: tabulka adres (= partition table) která určuje pozice kde která partitiona začíná a kde končí a posléze prostor, ve kterém lze jednotlivé partitiony umístit.

Úplný obrázek partition včetně partition table

Shrnutí

  • Partitioning je způsob členění jednoho fyzického zařízení na několik souborových systémů.
  • Každý souborový systém musí mít na fyzickém zařízení přesně vymezený rozsah
  • Adresování těchto souborových systémů se dělá skrz partition table

Uložiště a souborový systém

Operační systém, aplikace které v něm běží a jakákoliv vaše data musí být uložena na nějakém uložišti ve vašem zařízení. K tomu v dnešní době (2023) slouží běžně tyto komponenty:

  • HDD disk – jednotlivé bity jsou uloženy na fyzických „plotnách“ rozdílem v magnetické polaritě na konkrétním místě na disku (dostatečně silný magnet dokáže veškerá data v HDD disku zničit). Data jsou čtena/zapisována citlivým magnetem, který visí na takových hrábětech nad rotujícími plotnami.
  • SSD uložiště, SD karty, flash paměti – jednotlivé bity jsou uloženy v hradlech zachyceným elektrickým nábojem, který přetrvá i po odpojení od elektrického zdroje.

Jak uložiště fungují uvnitř je nám jedno

Každý disk má ještě nějakou svoji vlastní vnitřní strukturu a uspořádání, podle které se bity ukládají na fyzické vrstvě.

Na HDD je více fyzických ploten, data se ukládají od nejspodnější po nejsvrchnější, ve stopách a podobně. Na SDD jsou bity seskupovány do bloků, tyto bloky pak do ještě větších bloků a podobně, každý výrobce tyto bloky různě organizuje, atd. apod.

Tyto detaily jsou nám úplně ukradené a každé uložiště můžeme zjednodušit pouze na nějaký bitový rozsah. Disk s kapacitou 256 GB (ať už HDD nebo SSD) si můžeme představit jako uložiště pro obrovský rozsah bitů (256 GB = nějaké miliardy miliard bitů…) který má nějaký začátek a nějaký konec. Díky tomu můžeme říct, který bit je první, druhý, poslední atd.

Jak uložit data?

Řekněme, že máte k dispozici nový 256 GB disk a chcete si na něm uložit nějaká data. Kolik je 256 GB v bitech? To jsou nějaké šílené miliardy miliard, to se ani nepokusím znázornit. Řekněme, že si na tento disk chcete uložit nějaký text, třeba „Ahoj“ v ASCII znakové sadě.

Text „Ahoj“ v ASCII vypadá v bitech takto: 01000001 01101000 01101111 01101010

Kam ty bity chcete uložit? Kam chcete. Můžete to napsat na začátek, tzn. první bit textu „Ahoj“ zapsat na pozici 0. Nebo někam náhodně – třeba na pozici 6516843843846… Pokud zapomenete pozici, kde jste data uložili, o data jste prakticky přišli. Je to jako kdybyste zapomněli něčí adresu nebo telefonní číslo.

Kam si tu pozici zaznamenáte? Samozřejmě také někam na disk, ale kam? Musíte si vymyslet nějaký kód, nějaký systém, ve kterém určíte, kam a v jaké struktuře se na disk ukládají informace o umístění vašich dat.

Souborový systém: soubory a adresovatelnost

Nic vymýšlet si naštěstí nemusíte, těmto „systémům“ nebo standardům, jak se na disky ukládají data, se říká souborové systémy. Tento souborový systém je vlastně nová vrstva nad fyzickými bity uložiště.

Souborový systém jako samosatná vrstva

„Soubor“ není nic jiného, než nějaká hromada bitů, která je nějakým způsobem adresovatelná tzn. existuje způsob, jak se k těmto bitům dostat napřímo pomocí nějaké adresy.

Všechny adresy jsou uloženy podle předem daných pravidel na předem stanovené pozici v daném uložišti.

To znamená, že součástí každého souborového systému je vždy oddělení disku na dvě části: adresy a soubory.

Rozdělení uložiště na adresy a na data podle souborového systému.

Shrnutí

  • Vnitřní mechanismy jakými fyzická uložiště ukládají jednotlivé bity nás nezajímají
  • Zajímá nás pouze souborový systém, který nad uložištěm vytváří novou vrstvu díky které vznikají adresy a soubory

Race condition

V minulém díle jsem mluvil o tom, že procesor pomocí context switchingu přepíná mezi jednotlivými běžícími vlákny.

Otázka za 5 bludišťáků: lze přesně určit, které běžící vlákno doběhne dříve?

Odpověď: Prakticky nelze.

Je to dáno několika faktory:

  • První jednoduché procesory zpracovávaly instrukce napřímo, dnešní moderní procesory vykonávají jednotlivé instrukce v několika krocích v tzv. pipeline. V jednotlivých krocích pipelajny probíhají různé komplexní operace, včetně predikcí kdy se procesor snaží uhádnout výsledek instrukce (nebo následující instrukci) ještě před tím, než je instrukce spuštěna a to na základě krátkodobé statistiky, kterou si procesor ke spuštěným instrukcím udržuje. Délka této pipeliny je mezi 5 až 30 kroky. Vzhledem k prediktivním pipelajnám nelze odhadnout, jak daná instrukce proběhne rychle.
  • Context switching ovlivňují všechny možné faktory od typu operačního systému, aktuálního zatížení procesoru, počtu běžících procesů atd. apod.
  • Moderní procesory obsahují více jader, každé jádro obsahuje svoji vlastní pipelajnu.
  • Moderní procesory umí regulovat svoji frekvenci (pro ekonomický provoz) a umí to dokonce pouze pro konkrétní vlákna
  • Různé instrukce trvají různě dlouho. Procesor má sice jednu danou frekvenci např. 5 GhZ, jenže toto nelze převést přímo na počet instrukcí, i když to pro zjednodušení stačí.
    5 GhZ znamená, že procesor zvládne 5 miliardů „ticků“ za vteřinu a různé instrukce procesoru mají odlišný počet ticků, za který se dokončí.

A nyní se dostáváme k tomu, co je Race condition – v češtině překládáno jako „souběh“ ale nikdy jsem neslyšel žádného ajťáka, který by tomu takhle říkal.

Race condition je jakákoliv situace, kdy máte minimálně dvě běžící vlákna a nevíte, které skončí dřív. Je prakticky nepředvídatelné, které vlákno z libovolného počtu běžících vláken skončí jako první.

Příklad ad absurdum:

Řekněme, že máte aplikaci, která obsahuje hlavní vlákno a jedno vedlejší, celkem 2 vlákna

1. vlákno obsahuje instrukci Počkej 100 vteřin.
Po 100 vteřinách vlákno skončí a zanikne.

2. vlákno obsahuje instrukci Počkej 1 vteřinu
Po 1 vteřině vlákno skončí a zanikne

Nyní chcete změřit, které vlákno skončí dřív.

Toto se zdá, jako absurdní příklad, že? Samozřejmě, že 2. vlákno skončí dřív. A v 99,99% případů to nejspíš i tak bude.
Jenže nemáte 100% záruku!

A to je ta pointa: bez použití dalších technik (o kterých budu psát později) nemáte nikdy 100% záruku, že nějaké vlákno skončí dřív, než to první.

V počítači se totiž kdykoliv může stát například něco takového:

– Spustí se vlákno A – počkej 1 vteřinu
– Spustí se vlákno B – počkej 100 vteřin
– Vlákno A je přiřazené k jádru X
– Vlákno B je přiřazené k jádru Y
– V jádru X však právě teď probíhá proces z úplně jiného vlákna patřícímu jinému procesu. Toto vlákno obsahuje výpočetně náročné instrukce a OS se rozhodne, že toto vlákno musí nejdřív doběhnout a že v rámci jádra X nepřidělí čas žádnému jinému vláknu.
– Na jádru Y žádné náročné procesy neprobíhají a vlákno B tedy spokojeně běží a odpočítává 100 vteřin.
– Za 100 vteřin skončí vlákno B
– Za 863 vteřin skončí zahlcení procesoru, proces skončil.
– Za 863.02 vteřiny začne vlákno A odpočítávat jednu vteřinu
– Za 864.02 vteřiny skončí vlákno A

Jednoduchá poučka zní: pokud potřebujete při programování aplikací zjistit, které vlákno doběhne dřív, děláte něco špatně. Samozřejmě v programování existují techniky, ve kterých můžete vlákna synchronizovat takže třeba můžete v jednom vlákně říct, že v určitém bodě musí jedno vlákno počkat, dokud se něco nestane v druhém vlákně. O tom ale budu psát až se budeme učit programovat.

Pokud neprovádíte nějakou formu synchronizace, vlákna běží v nepředvídatelném čase a nelze určit, která instrukce ve kterém vlákně proběhne dřív.

Souhrn

  • Race condition je pojmenování situace, kdy se snažíte zjistit, které vlákno doběhne dřív.
  • Kdy které vlákno doběhne nelze (bez dalších technik jako např. synchronizace) předvídat.
  • Běžící vlákna lze různými způsoby synchronizovat ale bez této synchronizace nelze určit, která instrukce ve kterém vlákně proběhne dřív.
  • Běžící vlákna, která nejsou synchronizována, lze připodobnit k závodu formulí v mlze, který sledujete z tribuny na cílové čáře. Vidíte start a vidíte, kdo do cíle dojel dřív. Nemáte ale žádnou možnost jakkoliv ovlivnit, který z jezdců formule bude rychlejší, zkušenější a nebo který má lepší formuli.

Multithreading

V minulém díle jsem pojmenoval pojmy jako proces, entry point, vlákno, hlavní vlákno a stavy vláken.

Operační systém má na starost evidenci, vytváření a přidělování vláken. Jak se ale operační systém rozhoduje, které vlákno má zrovna běžet?

HALT instrukce

Už víte, že procesor je zařízení, do kterého se neustále sypou instrukce. Jakmile zapnete počítač a nic na něm neděláte tak to operační systém kompenzuje HALT instrukcí, která prostě jen pozastaví procesor na krátký časový úsek, třeba 0,0001 vteřiny.

Context switching

Jakmile začnou v počítači vznikat další vlákna, operační systém jednoduše sníží počet HALT instrukcí a uvolní výpočetní kapacitu procesoru novému vláknu.

Vláken může běžet najednou libovolné množství – jsme omezeni pouze pamětí a výkonem procesoru.

Jenže bacha – vzpomeňte si, jak funguje procesor: zpracovává instrukce za sebou. Procesor (jednoduchý, jednojádrový procesor) neumí zpracovávat několik instrukcí současně. Jasně, umí zpracovat 5 miliard instrukcí za vteřinu ale neumí zpracovat 2 instrukce najednou, ve stejný čas. Instrukce zpracovává v sérii (za sebou) nikoliv paralelně (vedle sebe).

Jak to tedy operační systémy dělají, že umožňují paralelní běh několika vláken najednou?

Neumožňují.

OS neustále přepíná mezi tím, které vlákno má zrovna přidělený čas procesoru. Tomuto přepínání se říká context switching a schopnosti takto spouštět jednotlivá vlákna se říká multithreading.

Na tomto jednoduchém schématu je vyznačeno, jak se vláknům přiděluje čas procesoru. Na oko to může vypadat, že obě vlákna běží paralelně, ve skutečnosti chvíli běží první vlákno (Thread #1), chvíli druhé (Thread #2).
Zdroj: Wikipedie

To je celé.

Plyne z toho jeden závěr: se zvyšujícím se počtem běžících vláken se zpomaluje rychlost každého běžícího vlákna, jinými slovy, zpomaluje se na oko práce s celým počítačem.

Moderní procesory jsou však tak brutálně rychlé, že z pohledu lidského vnímání na nich mohou běžet stovky vláken „najednou“. Ve zlomku vteřiny procesor přidělí dostatečný čas každému vláknu a z pohledu uživatele všechny vlákna běží paralelně.

Obrázek níže ilustruje celkovou kapacitu procesoru. Pokud má procesor 5 GHz, pak zvládne 5 miliard instrukcí za vteřinu. Operační systém a veškerý software, který pod OS běží, si díky context switchingu bere kousek této kapacity pro sebe.

OS Scheduler

OS Scheduler je název pro tu část operačního systému, která se stará o správu běžících procesů, vláken a o context switching.

V IT je však scheduler obecnější název pro jakýkoliv mechanismus, který má na starosti nějaké plánování.

Vícejádrové procesory

Moderní procesory jsou ve skutečnosti „multičipy“, které obsahují několik „podprocesorů“ kterým se říká „jádra“. Například „procesor s 8 jádry“ ve skutečnosti lze označit za „8 procesorů“, které běží paralelně, každý s frekvencí 5 miliard instrukcí za vteřinu.

Vícejádrových procesory umožňují, aby instrukce skutečně probíhaly paralelně. V jakém jádru ale běží jaké konkrétní vlákno si automaticky určuje operační systém. Windowsy a Linuxy umožňují přiřazení vlákna na konkrétní jádro pomocí tzv. afinity ale za celou svoji praxi jsem nikdy neměl situaci, že bych potřeboval afinitu vlákna nastavovat.

Multithreading je podporován v každém jádře. V každém jádře stále může běžet více vláken najednou ale díky většímu počtu jader lze provozovat větší počet vláken bez znatelné ztráty výkonu v porovnání s jednojádrovým procesorem.

Nelze ale automaticky říct, že procesor s více jádry je výkonnější, než jednojádrový procesor. Záleží na použití.

Aplikace, která spouští značné množství vláken, jako např. počítačová hra, bude těžit z vyššího počtu jader víc, než z jednojádrového procesoru. Aplikace, ve které běží jen jedno vlákno, bude běžet stejně rychle na jednojádrovém i vícejádrovém procesoru.

Shrnutí

  • HALT je instrukce, kterou operační systém v procesoru spouští, pokud zrovna nemá co dělat. Tato instrukce na velmi krátký časový úsek procesor pozastaví.
  • Procesor, nebo jádro procesoru, zpracovává instrukce vlákna v sérii = za sebou = nejdřív zpracuje jednu instrukci a pak tu, která za ní následuje.
  • Multithreading je schopnost spouštět několik vláken na jednou – ve skutečnosti operační systém neustále přepíná přidělený čas procesoru běžícím vláknům. Tomuto přepínání se říká context switch.
  • Moderní procesory jsou ve skutečnosti „multiprocesory“ – je to několik procesorů najednou. Těmto podprocesorům se říká „jádra“.
  • Operační systém určuje, jaké vlákno běží v jakém jádře.

Proces a vlákno

Určitě jste sami několikrát zažili postup instalace nějaké aplikace/programu. Nejdřív stáhnete nějaký soubor, ten pak spustíte, něco se nainstaluje a vy pak ten program spustíte.

Instalátory jsou samy o sobě nějaké programy. Aplikace, která neběží, je pouze seznam instrukcí v nějakém úložišti jako třeba HDD/SDD.

Jakmile spustíte nějakou aplikaci tak OS udělá několik věcí:

  • OS vytvoří nový proces
  • V dané aplikaci se najde entry point a přidělí se mu hlavní vlákno pod kterým se proces rozeběhne

Proces

Proces je prostě jenom pojmenování pro běžící aplikaci. To je fakt úplně všechno. Každý proces může spustit podproces ale Windowsy a Linuxy s podprcesy pracují odlišně.

  • Linux: jakmile hlavní proces skončí tak podprocesy skončí s ním
  • Windows: podprocesy běží i po skončení hlavního procesu

Proces ale není to co běží, proces je jenom způsob seskupování běžících aplikací a jejich vláken. To co „běží“ je ve skutečnosti hlavní vlákno. Viz. níže.

Entry point

Každá aplikace obsahuje entry point, což je místo, kde začínají instrukce tohoto programu. Hlavní vlákno procesu začíná spouštět instrukce v entry pointu.

Vlákno (thread)

Vlákno nebo anglicky „thread“ je něco jako „běžící jednotka“ nebo „přidělený procesor“ nebo přidělená kapacita spouštět instrukce. Vlákno se spustí a spouští instrukce které jsou k tomuto vláknu přidělené. Po provedení všech instrukcí vlákno skončí.

Vlákno bez instrukcí se po nastartování ihned ukončí. Seznam vláken si operační systém spravuje v paměti (v kernel space).

U vlákna se tedy rozlišuje jeho stav který může být:

  • Běžící (running)
  • Ukončený (terminated/killed/stopped atd.)

Toto jsou dva základní stavy, které jsou společné pro Windowsy i Linuxy. Oba operační systémy podporují i nějaké další stavy jako např. „pozastavené“, „přerušitelné“, „nepřerušitelné“, to už je ale v rámci této kapitoly nepodstatný detail.

Hlavní vlákno

Hlavní vlákno je vlákno, které z pohledu OS reprezentuje běžící program. Proces je pouze kategorizace, „aplikace/program“ to je jen názvosloví, jak tomu říkají lidé. Co ale skutečně v aplikaci běží je jeho hlavní vlákno.

Běžící aplikace = běžící hlavní vlákno aplikace.

Proč ale „hlavní“?

Protože jakákoliv aplikace – respektive jakékoliv vlákno – si může říct operačnímu systému o vytvoření dalšího vlákna.

Ale pozor: jakmile hlavní vlákno skončí, skončí i všechna ostatní vlákna a celý proces! Toto je stejné pro Windows i pro Linuxy.

Proces se skládá vždy minimálně z jednoho běžícího hlavního vlákna. Ukončení hlavního vlákna = ukončení procesu.

Pozor na to, nepleťte si vlákna a procesy. Proces je jen obal okolo hlavního vlákna, potažmo dalších vláken, které v rámci procesu běží. Při skončení hlavního vlákna jsou ukončena i všechna ostatní vlákna, která v procesu patří.

OS normálně nedovoluje procesům, aby jejich vlákna četla data z vláken jiných procesů nebo vlákna jiných procesů ovlivňovala. V rámci jednoho procesu je to ale možné. Každá aplikace má totiž od OS přidělený svůj kus v paměti a co si každá aplikace ve svém kusu paměti dělá a kolik vláken ji tam běží, to je její odpovědnost.

Možná se teď ptáte:

  • Proč aplikace potřebuje víc vláken?
  • Jak je vůbec možné, že aplikace běží s více než jedním vláknem? Procesor přeci zpracovává instrukce za sebou. Nemůže zpracovávat instrukce paralelně, ne?
  • A jak je vůbec možné, že v rámci OS může běžet víc aplikací? Tedy víc jak jedno vlákno?

O tom si povíme v příštím díle.

Zajímavost: „Fork bomb“ je název pro záškodnickou aplikaci, která obsahuje jen tuto instrukci: Instrukce A: vytvoř vlákno, jehož obsahem je instrukce A. „Fork bomb“ tedy vytváří vlákna donekonečna, dokud OS nedojde paměť nebo výpočetní kapacita procesoru a počítač se úplně odstaví. („fork“ je název Linuxové funkce pro vytvoření nového procesu)

Shrnutí

  • OS při spuštění aplikace vytváří proces. Proces je pouze kategorizace a pojmenovávání běžících aplikací.
  • Aplikaci je přiděleno hlavní vlákno na entry pointem.
  • Entry point je místo, kde začínají instrukce aplikace.
  • Vlákno je základní běžící jednotka.
  • Jakékoliv vlákno může zažádat OS o vytvoření dalšího vlákna se zadanými instrukcemi.
  • Vlákno může být ve 2 základních stavech: běžící a ukončené. Windows a Linuxy podporují u vláken další různé stavy s různými pravidly, jak se z jednoho stavu lze dostat do jiného.
  • Skončení hlavního vlákna znamená konec celého procesu a všech jeho vláken.