Hlavní navigace

Do útrob distribuce Slax

8. 1. 2013
Doba čtení: 8 minut

Sdílet

Slax je živá linuxová distribuce běžící z CD či USB, kterou asi nemusíme dlouze představovat. Jde pravděpodobně o jedinou 200MB distribuci, do které se vešel desktop s prostředím KDE 4. Pojďme se společně s Tomášem Matějíčkem podívat do útrob Slaxu a nahlédnout pod pomyslné pokličky v jeho linuxové kuchyni.

Článek vznikl jako volný překlad dokumentace Slax Internals.

Adresářová struktura

Než si ukážeme a vysvětlíme, jak Slax technicky funguje, podívejme se nejdříve na jeho adresářovou strukturu, kterou je možné vidět hned po vypálení na CD nebo po rozbalení Slaxu na USB disk. Všechny datové soubory Slaxu jsou kvůli přehlednosti uloženy na bootovacím médiu v jediném adresáři. Asi nikoho nepřekvapí, že se tento adresář jmenuje ‚slax‘.

Na následujícím schématu je zobrazena zjednodušená adresářová struktura. Uvedeny jsou jen ty soubory a adresáře, které nás momentálně zajímají, adresáře jsou tučně, soubory pak kurzívou.

slax
├─── boot
│    ├─── isolinux.bin
│    ├─── syslinux.cfg
│    ├─── initrfs.img 
│    ├─── vmlinuz
│    └─── ...
├─── changes
├─── rootcopy
├─── 01-core.sb
├─── 02-xorg.sb
├─── 03-kdeps.sb
├─── 04-kde.sb
└─── ...

Bootujeme linuxové jádro

Co se děje při startu Slaxu? Když BIOS vašeho počítače začne s bootováním, jeho jediným úkolem je zavedení zavaděče SYSLINUX. Ten je ukryt v souboru isolinux.bin nebo ldlinux.sys, podle média, které jste použili. CD/DVD používá isolinux.bin, na USB disku najdete  ldlinux.sys.

Jakmile je SYSLINUX spuštěn, řídí se obsahem svého konfiguračního souboru syslinux.cfg. Ve Slaxu tento soubor obsahuje příkazy k zobrazení bootovacího loga se čtyřlístkem, případně menu, pokud uživatel stiskne klávesu před vypršením časového limitu. Jakmile počítadlo dosáhne nuly nebo uživatel opustí menu, SYSLINUX načte do paměti dva soubory: vmlinuz (linuxové jádro) a initrfs.img (základní kořenový svazek, takzvaný root filesystem). Postup tohoto načítání je zobrazen pomocí řady teček, které se objevují na obrazovce. Jakmile je načítání ukončeno, spustí se vmlinuz a jádro začne startovat.

Před initem

Za normálních okolností, když běžná linuxová distribuce startuje z disku, připojí jádro kořenový oddíl a spustí se hlavní proces /sbin/init, který se pak postará o samotný start celého systému. Slax má ale jiné podmínky – nemá k dispozici žádný disk, ze kterého by kořenový oddíl připojil. Jádro ale pořád potřebuje nějaký proces init, který by spustilo. Kde ho vzít? Právě k tomuto účelu slouží initrfs.img. Jde o komprimovaný CPIO archiv, který obsahuje několik adresářů a souborů, včetně základních linuxových nástrojů a samotného initu.

Jakmile je tedy linuxové jádro v pořádku zavedeno a má plnou kontrolu nad počítačem, je jeho posledním úkolem najít zmíněný CPIO archiv v paměti (nahrál jej tam ze souboru initrfs.img zavaděč SYSLINUX, jak si jistě vzpomínáte), rozbalit ho (do paměťového prostoru, který se chová jako dočasný kořenový svazek, jmenuje se initramfs) a spustit odtud dočasný proces  /init.

Únik z initramfs

V tuhle chvíli už nám běží linuxová jádro, a initramfs oblast v paměti je naplněna dočasným kořenovým souborovým systémem s minimem základních příkazů a dočasným initem, který právě nastartoval.

Kořenový systém v initramfs není ideální, protože nepodporuje systémové volání pivot_root, které je velmi důležité a budeme jej potřebovat později. Potřebujeme tedy nějak přepnout z initramfs někam jinam. Abychom toho dosáhli, připojí si dočasný init do adresáře /m souborový systém tmpfs. Do něj zkopíruje všechny soubory z initramfs včetně sebe sama a poté použije switch_root, aby z /m udělal nový kořenový svazek. Nakonec se z nového umístění init znovu spustí.

Postup ukazuje následující schema. Hvězdička znázorňuje, co se přesunulo a kam.

initramfs jako kořen:  ->  initramfs po přesunu to tmpfs:  ->  tmpfs po switch_root:

(initramfs)               (initramfs)                         (tmpfs)
/                          /                                   / *
├─── bin                   └─── m (tmpfs) *                    ├─── bin
│    ├─── sh                    ├─── bin                       │    ├─── sh
│    ├─── mount                 │    ├───sh                    │    ├─── mount
│    └─── ...                   │    ├───mount                 │    └─── ...
├─── dev                        │    └─── ...                  ├─── dev
├─── lib                        ├─── dev                       ├─── lib
│    ├─── cleanup               ├─── lib                       │    ├─── cleanup
│    └─── ...                   │    ├─── cleanup              │    └─── ...
├─── mnt                        │    └─── ...                  ├─── mnt
├─── m (tmpfs) *                ├─── mnt                       ├─── memory
├─── memory                     ├─── memory                    ├─── proc
├─── proc                       ├─── proc                      ├─── sys
├─── sys                        ├─── sys                       └─── init
└─── init                       └─── init

Přestože celá tahle komplikovaná akce může vypadat divně (skončili jsme vlastně tam, kde jsme začali), změna je podstatná. Náš kořenový souborový systém je na tmpfs místo initramfs, takže operace pivot_root bude fungovat, až ji budeme potřebovat.

Možná se divíte, že je stále kořenový svazek označen jako dočasný. Je to tím, že jsme stále na úplném začátku startu a pravý kořenový oddíl si sestavíme mnohem později zkombinováním komprimovaných obrazů pomocí AUFS.

Slax hledá svá data

Než může začít init proces hledat soubory Slaxu na dostupných médiích, musí si nejprve připravit pracovní prostředí. Proto si připojí souborové systémy proc a sysfs do adresářů /proc a /sys. Pomocí modprobe jsou také zavedeny některé důležité jaderné moduly jako aufs, squashfs a loop. Poté jsou také vytvořena zařízení v /dev  pomocí příkazu mdev. Cesta k binárce mdev je také poslána do /proc/sys/kernel/hotplug, aby mohlo jádro samo vytvářet nové soubory zařízení v /dev, jakmile se objeví nové diskové oddíly. To je nutné zejména pro oddíly na USB zařízeních, kterým trvá několik sekund, než se plně zinicializují.

Jakmile jsou datová zařízení dostupná skrze soubory v /dev, je použit příkaz blkid, který odfiltruje pouze ty oddíly, které mohou být připojeny. Tedy takové, které obsahují souborový systém podporovaný právě běžícím jádrem. Oddíly jsou jeden po druhém prozkoumány (připojeny do /memory/data/), dokud se na některém z nich nepodaří najít adresář ‚slax‘ se správným obsahem. Poté jsou všechny soubory s příponou .sb (Slax Bundle) zpracovány tak, že je pro každý z nich vytvořen adresář v /memory/bundles/  a každý soubor (vlastně komprimovaný obraz se squashfs) je do svého adresáře připojen.

(tmpfs)
/
├─── bin
├─── dev
├─── ...
├─── memory
│    ├─── bundles
│    │    ├─── 01-core.sb (squasfhs mount) < ───┐
│    │    │    ├─── bin                         │
│    │    │    ├─── etc                         │
│    │    │    ├─── sbin                        │
│    │    │    └─── ...                         │
│    │    ├─── 02-xorg.sb ......................│...
│    │    │    ├─── etc                         │  :
│    │    │    ├─── usr                         │  :
│    │    │    └─── ...                         │  :
│    │    ├─── 04-kde.sb .......................│..:...
│    │    │    ├─── usr                         │  :  :
│    │    │    └─── ...                         │  :  :
│    │    └─── ...                              │  :  : loop
│    ├─── data (slax device mounted here)       │  :  : mounts
│    │    └─── slax                             │  :  :
│    │         ├─── boot                        │  :  :
│    │         ├─── changes                     │  :  :
│    │         ├─── rootcopy                    │  :  :
│    │         ├─── 01-core.sb ──── > ──── > ───┘  :  :
│    │         ├─── 02-xorg.sb ....................:  :
│    │         ├─── 04-kde.sb ........................:
│    │         └─── ...
│    ├─── changes (empty yet)
│    └─── union (empty yet)
├─── proc (procfs mounted here)
├─── sys (sysfs mounted here)
└─── init

Vše se spojuje pomocí AUFS

Různé části výsledného kořenového systému tedy máme připojené (pouze ke čtení) v podadresářích adresáře  /memory/bundles/. Základní systémové utility a knihovny jako /bin/bash či /lib/ld-linux.so jsou umístěny v /memory /bundles/01-core.sb/, soubory k prostředí KDE zase najdete v /memory/bundles/04-kde.sb/  a tak dále. Jejich zkombinování do jednotného souborového systému, který je navíc zapisovatelný, je možné jen díky AUFS – union-like souborovému systému, jehož autorem je pan Junjiro Okajima. AUFS umí vzít několik adresářů (nazývaných větve) a spojit je do jednoho adresáře. To přesně se také stane, adresáře se sloučí, přidá se k nim adresář /memory/changes/ a to celé se připojí do /memory/union/, jak je znázorněno na následujícím schématu.

(tmpfs)
/
├─── ...
└─── memory
     ├─── bundles
     │    ├─── 01-core.sb ───────── > ──────┐
     │    ├─── 02-xorg.sb ..................│.......
     │    ├─── 04-kde.sb ...................│......:........
     │    └─── ...                          │      :       :
     ├─── changes ──────── > ───────┐       │      :       :
     ├─── ...                       ˅       ˅      :       :
     └─── union < ═══════ < ═══════ < ───── < ─────┘ < ────┘
          ├─── bin            AUFS
          ├─── etc            mount
          ├─── mnt
          ├─── root
          ├─── sbin
          ├─── usr
          ├─── ...
          └─── var

Změny

Prázdný adresář /memory/changes/ je zapisovatelný, takže celý nový AUFS v /memory/union/  se stane také zapisovatelným. Všechny nové či změněné soubory v něm jsou zkopírovány do adresáře changes těsně před tím, než je systém vytvoří či modifikuje. Jelikož se tento adresář také nachází v tmpfs (tedy v RAM), jsou všechny změny uloženy jen v paměti a při restartu se ztratí.

Pokud je ale Slax spuštěn ze zapisovatelného média, jakým je třeba USB disk, pozná to a připojí toto zapisovatelné médium do /memory/changes/ ještě před přidáním do AUFS. Nově vytvořené či změněné soubory se tak budou ukládat na USB zařízení místo do paměti RAM a restart je nesmaže. Tato vlastnost se jmenuje „trvalé změny“ (persistent changes) a je možné ji zapnout či vypnout v bootovacím menu.

Přepínáme na skutečný root

V tuto chvíli už máme plně zapisovatelný kořenový souborový systém v /memory/union/. Život dočasného initu se blíží ke konci. Použije systémová volání pivot_root a chroot, aby z /memory/union/  udělal nový kořenový svazek a ten starý přesunul do /mnt/live/. Hvězdičky a mřížky na diagramu níže označují, co se kam přesune. Nakonec se spustí opravdový /sbin/init z nového kořene AUFS a boot operačního systému Slax může konečně začít.

tmpfs kořen před pivot_root:                 aufs jako finální kořenový filesystem:

(tmpfs) #                                    (aufs) *
/                                            /
├─── ...                                     ├─── bin
└─── memory                                  ├─── etc
     └─── union (aufs) *                     ├─── mnt
          ├─── bin                           │    └─── live (tmpfs) #
          ├─── etc                           │         ├─── ...
          ├─── mnt                           │         └─── memory
          │    └─── live (empty)             │              └─── union (empty)
          ├─── root                          ├─── root
          ├─── sbin                          ├─── sbin
          │    ├─── ...                      │    ├─── ...
          │    └───init                      │    └─── init <- gets executed
          ├─── usr                           ├─── usr
          ├─── ...                           ├─── ...
          └─── var                           └─── var

Přidávání modulů za jízdy

Kořenový souborový systém je zapisovatelný, takže do něj můžeme instalovat softwarové balíčky klasickým způsobem, tedy jejich rozbalením. Existuje ale ještě jedna možnost, jak přidat soubory a adresáře do Slaxu, aniž bychom cokoliv rozbalovali a instalovali. Díky tomu, že Slax běží na AUFS jakožto kořenovém systému, můžeme vzít další komprimované obrazy squashfs, připojit je do adresářů mimo AUFS strom (třeba /mnt/live/memory/bundles/name.sb/, který leží v tmpfs) a použít příkaz remount k přidání nové větve do AUFS.

Všechny soubory a adresáře z nového squashfs modulu se okamžitě objeví, jako by v systému byly od začátku. Dekomprese se pak provádí na pozadí a jen na těch souborech, které právě potřebujeme.

Podobně můžeme některou z AUFS větví (připojený squashfs) odstranit dalším příkazem remount. Soubory, které byly součástí větve, okamžitě zmizí ze systému, čímž dojde k jejich praktickému odinstalování.

Vypínáme Slax

Když se Slax vypíná či restartuje, provádí stejné úkony jako každá jiná distribuce. Odpojuje všechny oddíly připojené uživatelem, ukončuje procesy a podobně. Bootovací zařízení ale může být stále připojeno kvůli trvalým změnám, takže je potřeba udělat ještě další kroky, než je počítač doopravdy vypnut nebo restartován.

root_podpora

Namísto vypnutí systému je tedy ještě spuštěn uklízecí skript, který se nachází v /mnt/live/lib/cleanup. Tento skript zavolá pivot_root a chroot, které už známe, a přepne kořenový svazek zase zpět na tmpfs. To je přesný opak toho, co jsme si popsali v kapitole Přepínáme na skutečný root.

Jakmile je kořenový svazek přepnut zpět na tmpfs, je spuštěn příkaz telinit u, který ukončí právě běžící init skript (jinak by blokoval odpojení AUFS) a nahradí jej znovu spuštěným skriptem cleanup. Kořenový svazek v AUFS už není nadále blokován, protože na něm nejsou otevřené soubory ani z něj neběží žádný proces. Je tedy možné jej odpojit, stejně jako všechny připojené squashfs a bootovací médium je vysunuto. Nakonec je počítač restartován či vypnut, podle toho, co si uživatel přál.

Byl pro vás článek přínosný?

Autor článku

Tomáš Matějíček je autorem několika více či méně známých projektů jak z oblasti operačních systémů, tak internetu. V současnosti samozvaný expert na Linux, Bash, PHP a MySQL.