Cvičení: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.

Na tomto cvičení se podíváme jak zjednodušit sestavování složitého software. A také se podíváme na práci se archivy a základy správy (diskových) úložišť v Linuxu.

Předstartovní kontrola

  • Umíte zpaměti používat shellové řídící struktury.

Průběžný příklad

Opět se vrátíme k našemu příkladu generování webových stránek a použijeme jej jako průběžný příklad pro většinu tohoto cvičení.

Opět použijeme jednodušší verzi, která vypadala takto:

#!/bin/bash

set -ueo pipefail

pandoc --template template.html index.md >index.html
pandoc --template template.html rules.md >rules.html
./table.py <score.csv | pandoc --template template.html --metadata title="Score" - >score.html

Všimněte si, že pro index a rules máme Markdown soubory, ze kterých vytvoříme HTML. Stránka score je generována z CSV souboru.

Příprava

Prosím, vytvořte fork repozitáře s webem, abyste si mohli příklady sami vyzkoušet (tento repozitář budeme ještě používat na jednom z dalších cvičení, takže ho zatím neodstraňujte).

Motivace pro používání systémů sestavení

V našem příkladu je celý web sestaven v několika krocích, kdy jsou stránky HTML generovány z různých zdrojů. To je vlastně velmi podobné tomu, jak se software sestavuje ze zdrojových kódů (uvažujme zdrojové kódy v jazyce C, které se kompilují a linkují dohromady).

Zatímco výše uvedené kroky nevytvoří spustitelný soubor (což je typický případ pro vývoj softwaru), reprezentují obvyklý scénář.

Sestavení softwaru se obvykle skládá z mnoha kroků, které zahrnují tak odlišné činnosti jako třeba:

  • kompilace zdrojáků do nějakého meziformátu
  • linkování výsledné binárky
  • vytvoření bitmapové grafiky v různém rozlišení z jediného vektorového obrázku
  • generování dokumentace ze zdrojového kódu
  • příprava souborů pro lokalizaci (překlad)
  • vytvoření samorozbalujícího archívu
  • nahrání softwaru na webový server
  • zveřejnění artefaktu v repozitáři balíčků

Skoro všechny jsou vlastně hrozně jednoduché. Složité je poskládat je dohromady. To jest, jak je spustit ve správném pořadí a jak spustit jednotlivé programy se správnými parametry (volbami).

Například, než může být vytvořen instalátor, musí být nachystané všechny ostatní soubory. Soubory pro překlad obvykle závisí na předzpracování nějakých zdrojových souborů, ale musí být nachystány než je vytvořena výsledná binárka. Atd.

I pro malé projekty může být množství kroků poměrně velké. Ale jsou – svým způsobem – nezajímavé: nechcete si je pamatovat. Chcete ten software sestavit!

Všimněte si, že vám s tím vším často pomůže vaše IDE — jediným kliknutím. Ne každý však používá stejné IDE a možná dokonce grafické rozhraní vůbec nemáte.

Kromě toho chcete obvykle spustit sestavení jako součást každé revize — typickým příkladem jsou pipeline GitLabu, které používáme pro testy: spouštějí se bez grafického rozhraní, přesto chceme software sestavit (a také otestovat). Kodifikace tohoto postupu ve skriptu sestavení to zjednodušuje prakticky pro každého.

Náš výše zmíněný skript build.sh je vlastně docela pěkný. Je snadno pochopitelný, neobsahuje žádnou složitou logiku a nový člen týmu nemusí zkoumat všechny drobné detaily a může prostě spustit jediný skript build.sh.

Skript je v pohodě, ale zbytečně přepisuje soubory i když nedošlo ke změně. V našem malém příkladu to nevadí (konec konců, počítač máme dost rychlý).

Ale ve větším projektu, kde bychom pracovali s tisícovkami souborů (klidně se podívejte na zdrojáky Linuxového jádra, Firefoxu nebo LibreOffice) je to velký rozdíl. Pokud se vstupní soubor nezměnil (třeba jsme změnili jen rules.md) nemusíme přetvářet ostatní soubory (nemusíme znovu vytvářet index.html).

Trochu teď skript rozšíříme.

...

should_generate() {
    local barename="$1"
    if ! [ -e "${barename}.html" ]; then
        return 0
    fi
    if [ "${barename}.md" -nt "${barename}.html" ]; then
        return 0
    else
        return 1
    fi
}

...

should_generate index && pandoc --template template.html index.md >index.html
should_generate rules && pandoc --template template.html rules.md >rules.html

...

Tohle můžeme udělat pro každý příkaz a zrychlíme tím vytváření webu.

Ale.

To je dost práce. A pravděpodobně by ušetřený čas by promrhán přepisováním našeho skriptu. Nemluvě o tom, že výsledek je děsivý. A dost náročný na údržbu.

Často také potřebujeme sestavit jen část projektu: např. jen vygenerovat dokumentaci (bez dalšího zveřejnění, třeba). Ačkoliv rozšíření skriptu tímto způsobem je možné, pro velké projekty to nebude fungovat dobře.

if [ -z "${1:-}" ]; then
    ... # build here
elif [ "${1:-}" = "clean" ]; then
    rm -f index.html rules.html score.html
elif [ "${1:-}" = "publish" ]; then
    cp index.html rules.html score.html /var/www/web-page/
else
    ...

Naštěstí je tu lepší cesta.

Existují speciální nástroje zvané build systémy (česky možná také nástroje pro řízení překladu/sestavení) které mají jediný účel: koordinovat proces sestavení. Uživateli dávají vysokoúrovňový jazyk, ve kterém může zachytit výše zmíněné kroky pro sestavení softwaru.

V tomto cvičení se zaměříme na make. make je poměrně starý systém, ale pořád je hojně používán. Je to také jeden z nejjednodušších nástrojů z této kategorie: hodně toho musíte připravit ručně, ale pro vysvětlení principů je to perfektní. Budeme mít plnou moc nad celým procesem a uvidíte, co se všechno děje.

make

Nejprve se přesuňte do kořenového adresáře (lokálního klonu vašeho forku) repozitáře příkladu s webem, prosím.

Soubory v tomto adresáři jsou prakticky stejné jako v našem shellovém skriptu výše, ale je zde jeden soubor navíc: Makefile. Všimněte si, že Makefile je napsán s velkým M, aby byl snadno rozlišitelný (ls v nelokalizovaném nastavení řadí velká písmena jako první).

Tento soubor je řídicím souborem pro sestavovací systém make, který dělá přesně to, co jsme se snažili napodobit v předchozím příkladu. Obsahuje posloupnost pravidel pro sestavování souborů.

K přesné syntaxi pravidel se dostaneme brzy, ale nejprve si s nimi pojďme pohrát. Spusťte následující příkaz:

make

Zobrazí se následující výstup (pokud jste už některé příkazy provedli ručně, může se výstup lišit):

pandoc --template template.html index.md >index.html
pandoc --template template.html rules.md >rules.html

make vypíše příkazy, které provede, a spustí je. Vytvořil pro nás webové stránky: všimněte si, že byly vygenerovány soubory HTML.

Soubor version.inc.html prozatím vůbec negenerujeme.

Spusťte znovu make.

make: Nothing to be done for 'all'.

Jak vidíte, make byl dost chytrý na to, aby rozpoznal, že vzhledem k tomu, že se žádný soubor nezměnil, není třeba nic spouštět.

Aktualizujte soubor index.md (funguje i touch index.md) a znovu spusťte make. Všimněte si, že soubor index.html byl znovu sestaven, zatímco soubor rules.html zůstal nedotčen.

pandoc --template template.html index.md >index.html

Tomuto postupu se říká inkrementální sestavení (sestavujeme pouze to, co bylo potřeba, místo abychom sestavovali vše od začátku).

Jak jsme uvedli výše: v našem malém příkladu to není příliš zajímavé. Jakmile však budou vstupních souborů tisíce, rozdíl bude obrovský.

Je také možné spustit make index.html a požádat o přestavbu pouze index.html. Sestavení je opět inkrementální.

Pokud si přejete vynutit sestavení, spusťte make s -B. Často se tomu říká nepodmíněné sestavení.

Jinými slovy, make nám umožňuje zachytit jednotlivé jednoduché příkazy potřebné pro sestavení projektu (bez ohledu na to, zda kompilujeme a linkujeme programy v jazyce C nebo generujeme webovou stránku) do uceleného skriptu.

Přestavuje pouze věci, které je třeba přestavět, a co je zajímavější, bere v potaz závislosti. Například pokud se scores.html generuje z scores.md, který se sestavuje z scores.csv, stačí zadat, jak se má sestavit scores.md z scores.csv a jak se má vytvořit scores.html z scores.md, a make zajistí správné pořadí.

vysvětlení formátu Makefile

Makefile je řídicí soubor pro systém sestavování s názvem make. V podstatě se jedná o jazyk specifický pro danou doménu (domain-specific language), který zjednodušuje nastavení skriptu bez nutnosti funkce should_generate, kterou jsme zmínili výše.

Na rozdíl od většiny programovacích jazyků make rozlišuje tabulátory a mezery. Veškeré odsazení v souboru Makefile musí být provedeno pomocí tabulátorů. Musíte se ujistit, že váš editor nenahrazuje tabulátory na mezery. To je také častý problém při kopírování kusů kódu z webového prohlížeče.

(Obvykle váš editor rozpozná, že Makefile je speciální název souboru, a sám se přepne na politiku pouze tabulátory.) Pokud místo toho použijete mezery, obvykle se zobrazí chyba typu Makefile:LINE_NUMBER: *** missing separator. Stop..

Soubor Makefile obsahuje posloupnost pravidel. Pravidlo vypadá takto:

index.html: index.md template.html
    pandoc --template template.html index.md >index.html

Název před dvojtečkou je cíl pravidla. Obvykle je to název souboru, který chceme vytvořit. Zde je to index.html.

Zbytek prvního řádku je seznam závislostí – souborů, ze kterých je cíl sestaven. V našem příkladu jsou závislostmi soubory index.md a template.html. Jinými slovy: když se tyto soubory (index.md a template.html) změní, musíme znovu sestavit index.html.

Třetí částí jsou následující řádky, které musí být odsazeny tabulátorem. Obsahují příkazy, které je třeba provést, aby byl cíl sestaven. Zde je to volání příkazu pandoc.

make spustí příkazy, pokud je cíl zastaralý. To znamená, že buď chybí cílový soubor, nebo je jedna či více závislostí novější než cíl.

Zbytek souboru Makefile je podobný. Jsou zde pravidla pro další soubory a také několik speciálních pravidel.

Speciální pravidla

Speciální pravidla jsou all, clean a .PHONY. Neurčují soubory, které mají být sestaveny, ale spíše speciální akce.

all je tradiční název pro první pravidlo v souboru. Říká se mu výchozí pravidlo a je použito, pokud spustíte make bez argumentů. Obvykle nemá žádné příkazy a závisí na všech souborech, které by měly být sestaveny jako výchozí.

clean je speciální pravidlo, které obsahuje pouze příkazy, ale žádné závislosti. Jeho účelem je odstranit všechny vygenerované soubory, pokud chcete vyčistit svůj pracovní prostor. Obvykle clean odstraní všechny soubory, které nejsou verzovány (tj. nejsou pod kontrolou systému Git).

To lze považovat za nesprávné použití programu make, které má však dlouhou tradici. Z pohledu make jsou cíle all a clean stále považovány za názvy souborů. Pokud vytvoříte soubor s názvem clean, speciální pravidlo přestane fungovat, protože cíl bude považován za aktuální (existuje a žádná závislost není novější).

Chcete-li se této pasti vyhnout, měli byste výslovně říci make, že cílem není soubor. To se provede tak, že jej uvedete jako závislost speciálního cíle .PHONY (všimněte si úvodní tečky).

Obecně je vidět, že make má spoustu zvláštností. Často je to tak u programů, které začaly jako jednoduchý nástroj a prošly si 40 lety postupného vývoje, kdy pomalu přibývaly funkce. Přesto je to jeden z nejpoužívanějších build systémů. Často také slouží jako back-end pro pokročilejší nástroje — ty vygenerují Makefile z přívětivější specifikace a delegují práci na make.

Cvičení

Rozšiřte soubor Makefile o volání generovacího skriptu pro stránku score.html. Nezapomeňte aktualizovat pravidla all a clean.

Řešení.

Podadresář out/ je prázdný (obsahuje pouze soubor .gitignore, který určuje, že všechny soubory v tomto adresáři budou systémem Git ignorovány a nebudou tedy zobrazeny v git status).

Aktualizujte Makefile pro generování souborů do tohoto adresáře. Důvody jsou zřejmé:

  • Vygenerované soubory nezaneřádí váš pracovní adresář (stejně z nich nechcete dělat commit).
  • Při synchronizaci s webovým serverem můžeme zadat celý adresář, který má být zkopírován (místo zadávání jednotlivých souborů).

Řešení.

Přidejte speciální (.PHONY) cíl upload, který zkopíruje vygenerované soubory na počítač v Rotundě. Vytvořte si tam (ručně) adresář ~/WWW. Jeho obsah bude dostupný jako http://www.ms.mff.cuni.cz/~LOGIN/.

Poznamenejme, že je třeba přidat potřebná oprávnění souborového systému AFS pomocí příkazu fs setacl (vizte cvičení 08).

Řešení.

Přidejte generování PDF z rules.md (pomocí LibreOffice). Všimněte si, že soffice podporuje parametr --outdir.

Zamyslete se nad následujícím:

  • Kam umístit mezisoubor ODT?
  • Má být pro generování souboru ODT vytvořeno zvláštní pravidlo, nebo má být provedeno jediným pravidlem se dvěma příkazy?

Řešení.

Zlepšení udržovatelnosti souboru Makefile

Soubor Makefile začíná obsahovat příliš mnoho opakujícího se kódu.

Ale i s tím vám může make pomoci.

Odstraňme všechna pravidla pro generování out/*.html z *.md a nahraďme jej tímto pravidlem:

out/%.html: %.md template.html
      pandoc --template template.html -o $@ $<

To je vzorové pravidlo (pattern rule), které zachycuje myšlenku, že HTML je generováno z Markdown. Znak procenta ve specifikaci závislostí a cíle představuje tzv. stem — proměnlivou (tj. měnící se) část vzoru.

V příkazové části používáme proměnné make. Proměnné make začínají na dolar jako v shellu, ale nejsou stejné.

$@ je skutečný cíl a $< je první závislost.

Spusťte make clean && make a ověřte, zda se web generuje i při použití vzorových pravidel.

Kromě vzorových pravidel rozumí make také (uživatelským) proměnným. Ty mohou zlepšit čitelnost, protože můžete oddělit konfiguraci od příkazů. Například:

PAGES = \
      out/index.html \
      out/rules.html \
      out/score.html

all: $(PAGES) ...
...

Všimněte si, že na rozdíl od shellu jsou proměnné expandovány pomocí $(VAR) konstrukcí. (S výjimkou speciálních proměnných, jako je $<.)

Nepřenositelná rozšíření

make je velmi starý nástroj, který existuje v mnoha různých implementacích. Dosud zmíněné funkce by měly fungovat s jakoukoli verzí make. (Přinejmenším s přiměřeně nedávnou verzí. Staré verze make neměly .PHONY ani vzorová pravidla.)

Poslední doplněk bude fungovat pouze v GNU make (ale ten je v Linuxu výchozí, takže by s ním neměl být žádný problém).

Soubor Makefile změníme takto:

PAGES = \
      index \
      rules \
      score

PAGES_TMP=$(addsuffix .html, $(PAGES))
PAGES_HTML=$(addprefix out/, $(PAGES_TMP))

Uchováváme pouze základní název každé stránky a počítáme výstupní cestu. $(addsuffix ...) a $(addprefix ...) jsou volání vestavěných funkcí. Formálně jsou všechny argumenty funkcí řetězce, ale v tomto případě jsou jména oddělená čárkami považována za seznam.

Všimněte si, že jsme přidali PAGES_TMP pouze pro zlepšení čitelnosti při prvním použití této vlastnosti. Za normálních okolností byste do PAGES_HTML přiřazovali přímo toto.

PAGES_HTML=$(addprefix out/, $(addsuffix .html, $(PAGES)))

To bude ještě užitečnější, až budeme chtít pro každou stránku vygenerovat také soubor PDF. Můžeme přidat vzorové pravidlo a vytvořit seznam PDF pomocí $(addsuffix .pdf, $(PAGES)).

Další příklady …

… jsou uvedeny na konci této stránky.

Správa uložiště

Než budete pokračovat, připomeňte si, že soubory se nacházejí v souborových systémech, což jsou struktury na skutečných blokových zařízeních (typicky discích).

Práce se souborovými systémy a blokovými zařízeními je nutná při instalaci nového systému, při záchraně dat z poškozeného zařízení nebo při prosté kontrole volného místa.

Běžné soubory a adresáře již znáte. V systému Linux však můžete najít i jiné typy souborů.

Symbolické odkazy

Linux umožní vytvořit symbolický odkaz na jiný soubor. Tento speciální druh souboru nemá žádný obsah a jen odkazuje na další soubor.

Zajímavou vlastností symbolických odkazů je, že jsou transparentní pro standardní I/O API. Pokud zavoláte Pythoní open na symbolickém odkazu, otevřete tím (transparentně) soubor, na který odkazuje. To je zamýšlené chování.

Účelem symbolických odkazů je umožnit jiný pohled na stejné soubory bez nutnosti cokoliv kopírovat a synchronizovat.

Například uvažujme, že přehrávač videí umí přehrávat jen soubory v adresáři Videos. Ve skutečnosti ale máme soubory jinde, protože jsou na sdíleném disku. S pomocí symbolického odkazu můžete složku Videos nasměřovat na skutečné úložiště a přehrávač nic nepozná. (Pro pořádek, neznáme žádný přehrávač s podobných chováním, ale existuje spousta jiných programů, kde podobná magie umožní jejich funkci i v prostředí, pro které nebyly navrženy.)

Symbolický odkaz je něco jiného než zástupce na ploše apod. Tito zástupci jsou ve skutečnosti normální soubory, kde kromě cílového souboru určíte třeba i ikonu apod. Symbolické odkazy fungují o úroveň níže.

Speciální soubory

Kromě toho také existují speciální soubory, které reprezentují fyzická zařízení, nebo soubory, které fungují jako kukátka do stavu systému.

Hlavním důvodem je, že je to tak mnohem jednodušší pro vývojáře. Nepotřebujete speciální programy na práci s diskem, nepotřebujete zvláštní program na zjištění stavu paměti. Prostě jen přečtete obsah dobře známého souboru a máte potřebná data.

Je také mnohem jednodušší takové programy testovat – můžete jim předhodit jiný soubor jen změnou cesty. Což je změna, která by neměla rozbít zbytek programu.

Soubory, které reprezentují stav systému, jsou typicky textové. Například soubor /proc/meminfo může vypadat takto:

MemTotal:        7899128 kB
MemFree:          643052 kB
MemAvailable:    1441284 kB
Buffers:          140256 kB
Cached:          1868300 kB
SwapCached:            0 kB
Active:           509472 kB
Inactive:        5342572 kB
Active(anon):       5136 kB
Inactive(anon):  5015996 kB
Active(file):     504336 kB
Inactive(file):   326576 kB
...

Tento soubor není nikde na disku, ale když soubor otevřete, Linux vytvoří jeho obsah za běhu.

Všimněte si, jak je informace strukturována: jde o textový soubor, takže nepotřebujete žádné speciální nástroje na přečtení. Navíc je obsah snadno čitelný pro člověka. Na druhou stranu, struktura souboru je pevně daná: co řádek, to záznam; klíč a hodnota jsou odděleny dvojtečkou. Snadné i pro zpracování programem.

Hierarchie souborových systémů

Nyní si stručně uvedeme některé důležité soubory, které najdete prakticky na každém počítači s Linuxem.

Nebojte se skutečně zobrazit obsah souborů, které zde uvádíme. hexdump -C je opravdu skvělý nástroj.

/boot obsahuje zavaděč pro načtení operačního systému. Po instalaci systému se tímto adresářem budete zabývat jen zřídka.

/dev je speciální adresář, ve kterém jsou umístěny soubory reprezentující hardwarová zařízení Pravděpodobně tam uvidíte soubor sda nebo nvme0, který reprezentuje váš pevný (nebo SSD) disk. Pokud tedy nepracujete pod superuživatelem, nebudete mít k těmto souborům přístup ale kdybyste je hexdumpovali, viděli byste bajty tak, jak jsou na skutečném pevném disku.

A zápis do takových souborů by přepsal data na disku!
Diskové nástroje v Linuxu totiž přijímají cesty k diskovým jednotkám, na kterých budou pracovat. Je tedy velmi snadné zadat jim soubor a předstírat, že se jedná o disk, který je třeba naformátovat. Toho lze využít k vytváření obrazů disků nebo k obnově souborů. A značně to zjednodušuje testování takových nástrojů, protože k testování není třeba mít skutečný disk.

Je důležité si uvědomit, že tyto soubory nejsou fyzickými soubory na disku (to by přece znamenalo mít disk uvnitř disku). Při čtení z nich to jádro rozpozná a vrátí správná data.

Tento adresář obsahuje také několik speciálních, ale velmi užitečných souborů pro vývoj softwaru.

/dev/urandom vrací náhodné bajty po neomezenou dobu. Pravděpodobně se interně používá uvnitř vašeho oblíbeného programovacího jazyka k implementaci jeho funkce random(). Zkuste na tomto souboru spustit hexdump (a připomeňte si, že <Ctrl>-C program ukončíte, až vás už náhodnost přestane bavit).

K dispozici je také /dev/full, který emuluje plný disk, /dev/null, který zahodí vše, co se na něj zapíše, nebo /dev/zero, který poskytuje nekonečný proud nulových bajtů.

/etc/ obsahuje konfiguraci systému. Většina programů v systémech UNIX se obvykle konfiguruje prostřednictvím textových souborů. Důvodem je to, že správce se musí naučit pouze jeden nástroj – dobrý textový editor – pro správu systému. Výhodou je, že většina konfiguračních souborů má podporu pro komentáře a je možné komentovat i konfiguraci. Pro představu, příklad takového konfiguračního souboru si můžete prohlédnout v /etc/systemd/system.conf.

Asi nejdůležitějším souborem je /etc/passwd, který obsahuje seznam uživatelských účtů. Všimněte si, že se jedná o textový soubor, kde každý řádek představuje jeden záznam a jednotlivé atributy jsou jednoduše odděleny dvojtečkou :. To je velmi jednoduché na čtení, velmi jednoduché na úpravu a velmi jednoduché na pochopení. Jinými slovy, princip KISS v praxi.

/home obsahuje domovské adresáře pro běžné uživatelské účty (tj. účty pro skutečné – lidské – uživatele).

/lib a /usr obsahují dynamické knihovny, aplikace a celosystémové datové soubory.

/var je určen pro volatilní data. Pokud byste chtěli na svůj počítač nainstalovat databázi nebo webový server, byly by jeho soubory uloženy zde.

/tmp je obecné umístění pro dočasné soubory. Tento adresář je automaticky vyčištěn při každém restartu, proto jej nepoužívejte pro trvalé uložení souborů. Mnoho systémů také automaticky odstraňuje soubory, které nebyly změněny v v posledních několika dnech.

/proc je virtuální souborový systém, který umožňuje ovládání a čtení nastavení jádra (operačního systému). Například soubor /proc/meminfo obsahuje poměrně podrobné informace o využití paměti RAM.

Opět platí, že /proc/* nejsou normální soubory, ale virtuální. Dokud je nepřečtete, jejich obsah nikde fyzicky neexistuje.

Když otevřete /proc/meminfo, jádro přečte své vnitřní datové struktury, připraví jeho obsah (pouze v paměti) a předá vám ho. Není to tak, že by se tento soubor fyzicky zapisoval každých 5 sekund nebo tak nějak, aby obsahoval nejaktuálnější informace.

Připojování a přípojné body (mounts, mount-points)

Každý souborový systém (ke kterému chceme přistupovat) je někde přístupný jako adresář (v porovnání například s písmenem jednotky v jiných systémech).

Pokud chceme přistoupit k zařízení /dev/sda3 pod /home, říkáme, že /dev/sda3 je připojené do /home, /home je potom přípojný bod a /dev/sda3 se často také nazývá svazek (volume).

Většina zařízení je připojena automaticky při startu. To zahrnuje / (kořen, root) kde je systém, ale také /home, kde jsou data uživatelů. Všimněte si, že /dev a /proc (kromě jiných) jsou speciální souborové systémy připojené do těchto míst. Takže, soubor /proc/uptime fyzicky vůbec neexistuje (tj. nenajdete žádný sektor na disku, který by obsahoval jeho data).

Souborové systémy připojené při startu jsou vyjmenovány v /etc/fstab. Málokdy jej budete muset měnit (obzvláště na notebooku) a tento – instalátorem vytvořený – soubor zůstane často beze změn. Všimněte si, že obsahuje identifikaci svazku (čili cestu k oddílu – partition), přípojný bod a další možnosti.

Když připojíte vyjímatelný USB disk, vaše desktopové prostředí ho obvykle připojí automaticky. Je možné ho připojit i ručně pomocí programu mount. Nicméně, program mount musí běžet pod uživatelem root (tohle vlákno vysvětluje, proč může připojení disku být bráno jako nebezpečná operace). Takže si s mountem můžete hrát jen na svých instalacích, kde máte oprávnění superuživatele. Na sdílených strojích to fungovat nebude.

Technická poznámka: výše uvedený text se může zdát jako protimluv, protože když mount vyžaduje rootovské heslo, jak může vaše desktopové prostředí (DE) připojit disk bez ptaní se na administrátorské heslo. Vaše DE totiž uvnitř nezavolá mount, ale použije jiný program (obvykle postavený nad Udisks nebo Polkitem) které dohromady ověří, že připojované zařízení je skutečně flash disk (nebo něco podobného) a uživatel je tzv. lokální (tj. aby to nefungovalo přes SSH). Pokud jsou tyto podmínky splněny, může disk pro uživatele připojit. Mimochodem, s UDisks můžete mluvit z shellu pomocí udiskctl.

Pokud chcete otestovat připojování ručně, připojte USB disk a odpojte ho v GUI (pokud byl připojen automaticky). Obvykle je zařízení připojeno někam do podadresáře v /media.

Vaše USB bude pravděpodobně dostupné jako /dev/sdb1 nebo /dev/sda1 podle toho, jaký disk v počítači máte (mrkněte do další části o lsblk, pokud chcete vidět seznam zařízení).

Připojování disků není omezeno jen na ty fyzické. V další části budeme mluvit o obrazech disků, ale jsou i jiné možnosti. Je možné připojit síťový disk (třeba NFS nebo AFS, které je používané na MFF) nebo dokonce vytvořit síťové blokové zařízení a to připojit.

Pokud máte virtualizovaný Linux, např. skrz VirtualBox, připojování disků je trochu složitější. Můžete přidat další virtuální disk a ten připojit ručně. Nebo můžete vytvořit tzv. pass-through a dovolit virtuálnímu stroji přistupovat přímo k vašemu fyzickému disku. Například ve VirtualBoxu je možné přistupovat k fyzickému oddílu skutečného disku ale pro experimentování je asi bezpečnější začít s USB pass-through, které zpřístupní USB “flešku” uvnitř hosta. Ale vždy se ujistěte, že fyzické zařízení v ten okamžik nepoužívá hostitel.

Práce s obrazy disků (disk images)

Linux má zabudovanou podporu pro práci s obrazy disků. To jest, se soubory, které obsahují zrcadlový obraz skutečného disku. Ve skutečnosti jste s nimi už také pracovali, pokud jste instalovali Linux do virtuálky nebo si stáhly obraz USB disk na začátku semestru.

Linux vám umožní takovýto obraz připojit, jako by to byl fyzický disk a dokonce na něm měnit soubory. To je potřeba pro následující činnosti:

  • Vývoj a ladění souborových systémů (což je spíše neobvyklá činnost)
  • Vykopírování souborů z obrazů disků virtuálních strojů
  • Obnova souborů z poškozených disků (neobvyklé, ale k nezaplacení)
Při obnově dat z poškozených disků je obvyklý postup ten, že se pokusíte zkopírovat disk tak, jak je, na co nejnižší úrovni (tj. kopírujete prostě bajty z disku bez jakékoliv interpretace). A teprve poté, co máte zkopírovaný obraz, se pokusíte o vlastní záchranu dat. To zabrání dalšímu poškozování disku a dá vám to větší množství času na vlastní obnovu dat.

Ve všech případech ale pro připojení obrazu disku stačí systému říct, že má k souboru přistupovat jako k dalšímu blokovému zařízení (vzpomeňte si na /dev/sda1 z příkladu výše).

Připojování disků ručně

sudo mkdir /mnt/flash
sudo mount /dev/sdb1 /mnt/flash

Vaše data budou viditelná v adresáři /mnt/flash.

Chcete-li disk odpojit, spusťte následující příkaz:

sudo umount /mnt/flash

Všimněte si, že spuštění příkazu mount bez argumentů vypíše seznam aktuálně připojených disků. K tomu nejsou vyžadována práva roota.

Určen svazku

Doposud jsme k určení svazku vždy používali název blokového zařízení (např. /dev/sdb1). Zatímco u malých systémů je to triviální, u větších to může být neuvěřitelně matoucí – názvy zařízení závisí na pořadí, v jakém systém disky objevil. Toto pořadí se může mezi jednotlivými spuštěními systému měnit a u vyměnitelných disků je ještě méně stabilní. Nechcete přece dopustit, aby náhodně připojený USB flash disk způsobil, že váš počítač nebude možné spustit :-).

Stabilnějším způsobem je odkazovat se na bloková zařízení pomocí symlinků pojmenovaných podle fyzického umístění v systému. Například /dev/disk/by-path/pci-0000:03:00.1-ata-6-part1 odkazuje na oddíl 1 disku připojeného k portu 6 řadiče SATA, který se nachází jako zařízení 00.1 na sběrnici PCI 0000:03.

Ve většině případů je ještě lepší popsat oddíl podle jeho obsahu. Většina souborových systémů má identifikátor UUID (univerzální jedinečný identifikátor, 128bitové číslo, obvykle náhodně generované) a často také štítek disku (krátký textový název). Můžete spustit lsblk -f pro zobrazení UUID a štítků všech diskových oddílů a poté zavolat mount s UUID=číslo nebo LABEL=štítek místo názvu blokového zařízení. Váš etc/fstab bude pravděpodobně odkazovat na vaše svazky jedním z těchto způsobů.

Připojování obrazů disků

Obrazy disků lze připojit téměř stejným způsobem jako bloková zařízení, pouze je třeba přidat k příkazu mount volbu -o loop.

Připomeňme, že mount vyžaduje práva roota (sudo), proto musíte následující příklad provést na svém vlastním počítači, nikoli na některém ze sdílených počítačů.

Chcete-li si to vyzkoušet, můžete si stáhnout tento FAT obraz disku a připojit jej.

sudo mkdir /mnt/photos-fat
sudo mount -o loop photos.fat.img /mnt/photos-fat
... (prace se soubory v /mnt/photos-fat)
sudo umount /mnt/photos-fat

Případně můžete spustit udisksctl loop-setup a přidat obraz disku jako vyměnitelnou jednotku, která se automaticky připojí na plochu:

# Pomoci udisksctl and automatickeho pripojovani v GUI
udisksctl loop-setup -f fat.img
# Tohle vytiskne asi /dev/loop0 ale cislo se muze lisit
# Ted to muzete pripojit v GUI (coz muze probehnout i automaticky)
... (prace se soubory v /run/media/$(whoami)/07C5-2DF8/)
udisksctl loop-delete -b /dev/loop0

Prográmky pro zjištění zaplněnosti disku

Základním nástrojem pro kontrolu volného místa na disku je df (disk free).

Filesystem     1K-blocks    Used Available Use% Mounted on
devtmpfs         8174828       0   8174828   0% /dev
tmpfs            8193016       0   8193016   0% /dev/shm
tmpfs            3277208    1060   3276148   1% /run
/dev/sda3      494006272 7202800 484986880   2% /
tmpfs            8193020       4   8193016   1% /tmp
/dev/sda1        1038336  243188    795148  24% /boot

Ve výchozím provedení (výše) se používají kilobajtové bloky. Pro čitelnější výstup jej spusťte s -BM nebo -BG (megabajty a gigabajty) nebo s -h, abyste jej nechali vybrat nejvhodnější jednotku.

Nezaměňujte df s du, které lze použít k odhadu využití místa souborem. Obvykle se du spouští jako du -sh DIR, aby se vypsalo celkové místo zabrané všemi soubory v DIR. Pro vypsání velikostí adresářů nejvyšší úrovně ve vašem $HOME můžete použít du -sh ~/*. Buďte však opatrní, protože prohledání všech adresářů může trvat poměrně dlouho.

Můžete si také všimnout, že využití prostoru hlášené pomocí du se nerovná součtu velikostí všech souborů. K tomu dochází proto, že soubory jsou organizovány v blocích, takže velikosti souborů jsou obvykle zaokrouhleny na násobek velikosti bloku. Kromě toho také adresáře spotřebovávají určitý prostor.

Chcete-li zjistit, jak jsou svazky (oddíly) vnořené a která bloková zařízení jádro rozpoznává, můžete použít příkaz lsblk. Na sdíleném počítači se zobrazí následující:

NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda      8:0    0   480G  0 disk
├─sda1   8:1    0     1G  0 part /boot
├─sda2   8:2    0   7.9G  0 part [SWAP]
└─sda3   8:3    0 471.1G  0 part /

To ukazuje, že počítač má 480G disk rozdělený na tři oddíly: malý /boot pro boostrapování systému, 8G odkládací oddíl a nakonec 470G zbývá pro systém a uživatelská data. Nepoužíváme samostatný svazek pro /home.

Mnoho dalších výstupních formátů najdete v manuálové stránce.

Zkoumání a úpravy svazků (oddílů)

Toto téma přenecháme více na pokročilý kurz. Pokud se chcete něco naučit sami, můžete začít s následujícími nástroji:

  • fdisk(8)
  • btrfs(8)
  • mdadm(8)
  • lvm(8)

Zkontrolujte si všemu rozumíte

Vyberte všechna pravdivá tvrzení. You need to have enabled JavaScript for the quiz to work.

Archivace a komprese souborů

S výše uvedeným tématem do jisté míry souvisí i způsob, jakým Linux řeší archivaci a kompresi souborů.

Archivace v systému Linux obvykle znamená sloučení více souborů do jednoho (pro snadnější přenos) a kompresi tohoto souboru (pro úsporu místa). Někdy se za archivaci považuje pouze první krok (tj. sloučení).

Ačkoli se tyto dvě činnosti obvykle provádějí společně, Linux je od sebe odděluje, protože to umožňuje kombinovat vhodné nástroje a formáty pro každou část práce. Všimněte si, že v jiných systémech, kde je preferovaným formátem soubor ZIP, jsou tyto činnosti sloučeny do jedné.

Nejpoužívanějším programem pro archivaci je tar. Původně byl primárně určen k archivaci na pásky, odtud jeho název: tape archiver. Spouští se vždy s volbou určující režim práce:

  • -c pro vytvoření nového archivu z existujících souborů,
  • -x pro extrakci souborů z archivu,
  • -t pro vypsání tabulky souborů uvnitř archivu.

Jméno archivu se zadává pomocí volby -f; pokud není jméno zadáno, archiv se čte ze standardního vstupu nebo zapisuje na standardní výstup.

Volba -v jako obvykle zvyšuje upovídanost programu. Například tar -cv vypíše jména souborů přidávaných do archivu, tar -cvv vypíše také atributy souborů (podobně jako ls -l). (Vše se vypíše na stderr, takže stdout lze stále používat pro archiv) Obyčejný tar -t vypíše pouze názvy souborů, tar -tv vypíše i atributy souborů.

Tímto způsobem lze vytvořit nekomprimovaný archiv:

tar -cf archiv.tar adresar_k_archivaci/

Komprimovaný archiv vytvoříte tak, že výstup příkazu tar předáte příkazu gzip:

tar -c adresar_k_archivaci/ | gzip >archiv.tar.gz

Protože je to však velmi časté, tar podporuje přepínač -z, který automaticky volá gzip, takže můžete psát:

tar -czf archiv.tar.gz adresar_k_archivaci/

tar má další přepínače pro další (de)kompresní programy: bzip2, xz atd.. Nejdůležitější je přepínač -a, který vybírá (de)kompresní program podle názvu archivního souboru.

Pokud chcete komprimovat jen jeden soubor, často se používá prostý gzip bez tar. Některé nástroje nebo rozhraní API mohou dokonce zpracovávat soubory komprimované pomocí gzip transparentně.

Chcete-li archiv rozbalit, můžete do tar rourou poslat gzip -d (dekomprimovat) nebo použít -z následujícím způsobem:

tar -xzf archive.tar.gz
Stejně jako mnoho jiných programů souvisejících se souborovým systémem přepíše tar existující soubory bez jakéhokoli varování.

Pro usnadnění práce s archivy vám doporučujeme nainstalovat si atool jako obecný obal kolem tar, gzip, unzip a mnoha dalších nástrojů. Například:

apack archive.tar.gz dir_to_archive/
aunpack archive.tar.gz

Všimněte si, že atool ve výchozím nastavení nepřepisuje existující soubory (což je další velmi dobrý důvod pro jeho použití).

Je dobré vždy archivovat jeden adresář. Uživatel, který váš archiv rozbalí, tak nebude mít vaše soubory roztroušené v aktuálním adresáři, ale přehledně připravené v jediném novém adresáři.

Pro vypsání souborů v archívu můžete spustit příkaz als.

Úlohy k ověření vašich znalostí

Očekáváme, že následující úlohy vyřešíte ještě před příchodem na cvičení, takže se budeme moci o vašich řešeních na cvičení pobavit.

Balíček aspell poskytuje nástroj pro kontrolu pravopisu, který lze použít z příkazového řádku.

Příkaz aspell list --master en čte standardní vstup a všechna slova s překlepy vypisuje na standardní výstup.

Rozšiřte svůj projekt web a zkontrolujte, zda se ve zdrojových stránkách nevyskytují překlepy.

Tato úloha rozšíří průběžný příklad, který jsme použili na tomto cvičení.

Očekáváme, že soubory zkopírujte do vašeho repozitáře s úlohami, kde jsou i automatické testy (prosté zkopírování celého adresáře je v pořádku).

Následující jste již provedli (ale je to také součástí automatických testů k tomuto úkolu):

  1. Generujte soubory index.html a rules.html z příslušných souborů *.md.
  2. Vygenerované soubory uložte do podadresáře out/.
  3. cíl clean odstraní všechny soubory v adresáři out/ (kromě souboru .gitignore).

Jako novou funkci očekáváme, že příklad rozšíříte o následující:

  1. Přesuňte zdrojové soubory do podadresáře src/. Toto je povinná část, bez tohoto přesunu nebude žádný z testů fungovat. Očekáváme, že soubory přesunute ručně, tj. ne během buildu. Cílem je trochu pročistit adresářovou strukturu. Takže byste měli mít commitnutý soubor 10/web/src/index.md ve vašem repozitáři.

  2. Generujte stránky ze souborů *.csv. Ze score.csv se již generuje soubor score.html. Předpokládáme, že přidáte soubory group-a.csv a group-b.csv, ze kterých se generují group-a.html a group-b.html (pomocí skriptu table.py jako pro score.csv). Soubory group-a.html a group-b.html by měly být vytvořeny automaticky.

  3. Generujte stránky ze souborů *.bin. Očekáváme, že soubor bude mít stejné základní jméno jako výsledný soubor .html a postará se o kompletní generování obsahu. Test si vytváří vlastní from-news.bin, vaše řešení musí používat vzorová pravidla (pattern rules) se správými stemy.

    Příklad níže se naschvál jmenuje jinak (news.bin), aby nedocházelo ke kolizi se souborem připraveným v rámci testu.

  4. Přidejte speciální cíl spelling, který zobrazí seznam překlepů v souborech Markdown. Předpokládáme, že pro tento úkol použijete aspell a jako hlavní jazyk použijete angličtinu.

Nápověda #1: seznam generovaných souborů si uložte do proměnné PAGES, protože Vám to zjednoduší údržbu vašeho Makefile.

Nápověda č. 2: následující příklad je jednoduchý příklad dynamicky generované webové stránky, která může být uložena v souboru src/news.bin. Skript je trochu záludný, protože data webové stránky jsou součástí skriptu a používá $0 pro čtení sám sebe (podobný trik se často používá při vytváření samorozbalovacích archivů pro Linux).

#!/bin/bash

set -ueo pipefail

sed '1,/^---NEWS-START-HERE---/d' "$0" | while read -r date comment; do
    echo "<dt>$date</dt>"
    echo "<dd>$comment</dd>"
done | pandoc --template template.html --metadata title="News" -

exit 0

# Actual news are stored below.
# Add each news item on a separate line
#
# !!! Do not modify the line NEWS-START-HERE !!!
#

---NEWS-START-HERE---
2023-05-01 Website running
2023-05-02 Registration open

Tento příklad vám může zkontrolovat GitLab skrz automatizované testy. Uložte vaše řešení jako 10/web/Makefile a commitněte ho (pushněte) do GitLabu.

Převeďte shellový skript pro sestavení spustitelného souboru (ze zdrojových kódů C) na skript založený na make.

Zdrojáky jsou v repozitáři s příklady (v 10/cc).

Soubor Makefile, který vytvoříte, musí nabízet následující funkce:

  • Výchozí cíl all vytvoří spustitelný soubor example.
  • Speciální cíl clean odstraní všechny mezisoubory (*.o) i výsledný spustitelný soubor (example).
  • Objektové soubory (.o) se vytvářejí pro každý soubor zvlášť, doporučujeme použít vzorová pravidla (pattern rules).
  • Objektové soubory musí být závislé na zdrojovém souboru (odpovídajícím souboru .c) i na hlavičkovém souboru.

Prosím, commitněte do repozitáře i zdrojové soubory.

U složitějších projektů v jazyce C se často Makefile alespoň částečně generuje (včetně správných závislostí na vkládaných hlavičkových souborech). V této úloze očekáváme, že všechny závislosti zadáte ručně, abyste ukázali, že make ovládáte.

Existuje pouze jeden hlavičkový soubor, takže seznam závislostí bude ve skutečnosti poměrně krátký.

Tento příklad vám může zkontrolovat GitLab skrz automatizované testy. Uložte vaše řešení jako 10/cc/Makefile a commitněte ho (pushněte) do GitLabu.

Učební výstupy

Učební výstupy podávají zhuštěný souhrn základních konceptů a dovedností, které byste měli umět vysvětlit a/nebo použít po každém cvičení. Také obsahují absolutní minimum, které je potřebné pro pochopení navazujících cvičení (a dalších předmětů).

Znalosti konceptů

Znalost konceptů znamená, že rozumíte významu a kontextu daného tématu a jste schopni témata zasadit do většího rámce. Takže, jste schopni …

  • vyjmenovat několik kroků, které jsou často nutné k vytvoření distribuovatelného softwaru (např. balíček nebo instalační program) ze zdrojového kódu a dalších základních artefaktů

  • vysvětlit, proč by sestavování softwaru mělo být reprodukovatelným procesem

  • vysvětlit, jak je možné zachytit proces sestavení softwaru

  • vysvětlit pojmy jazyků, které se používají pro zachycení potřebných kroků pro sestavení (distribuci) softwaru

  • vysvětlit, co je to obraz disku

  • vysvětlit, proč nejsou zapotřebí žádné speciální nástroje pro práci s obrazy disků

  • vysvětlit rozdíl mezi normálním (obyčejným) souborem, adresářem, symbolickým odkazem, souborem zařízení a souborem reprezentujícím stav systému (např. ze souborového systému /proc)

  • vyjmenovat základní adresáře nejvyšší úrovně v typické instalaci Linuxu a popsat jejich funkci

  • vysvětlit v obecné rovině, jak je strom adresářů vytvořen připojením jednotlivých (souborových) podsystémů

  • vysvětlit, proč Linux zachovává oddělení archivace a komprese (např. tar a gzip)

Praktické dovednosti

Praktické dovednosti se obvykle týkají použití daných programů pro vyřešení různých úloh. Takže, dokážete …

  • sestavit projekty používající make

  • vytvořit Makefile, který řídí sestavení jednoduchého projektu

  • používat pravidla se zástupnými znaky v Makefile

  • volitelné: používat proměnné v Makefile

  • volitelné: používat základní rozšíření GNU Make pro zjednodušení složitějších Makefile

  • připojit a pracovat s disky pomocí příkazu mount (jak s fyzickými tak s obrazy disků)

  • získat souhrnné informací o využití disku pomocí příkazu df

  • použít pro práci se standardními linuxovými archivy tar nebo atool

  • volitelné: použít lsblk ke zjištění informací o úložných zařízeních