Architektury jádra
Architektury jádra: monolit, mikrojádro a co z toho vyhrálo
V lednu 1992 poslal Andrew Tanenbaum do diskusní skupiny comp.os.minix příspěvek s nadpisem „LINUX is obsolete". Autor MINIXu v něm vysvětlil, že monolitické jádro je návrat do sedmdesátých let a že mikrojádra ten spor už vyhrála. Torvalds odpověděl a debata se táhla týdny.
Po třiceti letech je vidět, že pravdu měl každý z nich, jen jinde. Monolit vyhrál na serverech, desktopech a v telefonech. Mikrojádro vyhrálo tam, kde pád ovladače stojí životy: v autech, v letecké technice a - což je pěkná ironie - uvnitř každého moderního procesoru Intel běží MINIX.
Tahle stránka vysvětluje, čím se ty architektury liší a proč se ten rozdíl nedá zredukovat na „rychlost proti spolehlivosti". Neřeší samotnou hranici mezi režimy - to je na jádru a uživatelském režimu - ani jeden přechod přes ni, což je systémové volání. Bez těch dvou stránek zůstane zbytek jen názvoslovím.
Hranice ochrany, ne architektura
Uvnitř jádra sdílí veškerý kód jeden adresní prostor, běží v privilegovaném režimu a smí sáhnout na cokoli. Venku má vlastní adresní prostor a každý kontakt s okolím musí projít přes jádro.
Tohle je nejdůležitější věc na celé stránce: otázka nezní „monolit, nebo mikrojádro", ale co je uvnitř hranice ochrany. Každý kus kódu uvnitř může položit celý stroj. Každý kus kódu venku platí za každý přechod hranice.
Celý ten třicetiletý spor je o tuhle jednu směnu.
Monolitické jádro: co všechno je uvnitř
V Linuxu je uvnitř plánovač, správa paměti i s tabulkami stránek, souborové systémy, síťový zásobník, obsluha přerušení a ovladače všeho, co se dá k počítači připojit. Zdrojový strom má přes třicet milionů řádků a největší část zabírají právě ovladače.
Uvnitř je volání obyčejný skok. Když síťový zásobník potřebuje paměť, zavolá funkci: jednotky nanosekund, žádné přepnutí, žádné kopírování. Odtud plyne výkon monolitu.
Cena je, že chyba nemá kam spadnout. Ovladač, který zapíše mimo své pole, přepíše datovou strukturu plánovače. Nulový ukazatel v ovladači není výjimka procesu, ale panika jádra.
Moduly nejsou mikrojádro
Nejčastější omyl kolem architektur zní: Linux má moduly, takže je vlastně mikrojádro. Není.
lsmod # zavedené moduly a kolik věcí každý z nich používá
modinfo xfs # závislosti, parametry, licence a podpis modulu
sudo modprobe -r xfs # vysunutí; selže, dokud je svazek připojený
Modul se zavádí a odebírá za běhu, ale jakmile je uvnitř, běží ve stejném adresním prostoru se stejnými právy jako zbytek jádra. Vidí všechny symboly, může měnit cokoli, a když spadne, položí stroj stejně jako kód přeložený napevno.
Modul je otázka zavádění, ne ochrany. Distribuce ho používá proto, aby jedno jádro nabootovalo na tisících různých strojů. S izolací nemá nic společného.
Rada „přelož si ovladače napevno kvůli výkonu" je zbytek doby, kdy zavádění modulů něco stálo. Po zavedení je to tentýž kód se stejnými právy.
Mikrojádro: co zůstane, když zbytek vystrčíš ven
V jádře zůstane jen to, co bez privilegovaného režimu nejde: plánování, správa adresních prostorů a předávání zpráv. Zbytek - ovladače, souborové systémy, síť - jsou běžné procesy s vlastní pamětí.
Ovladač disku, který sáhne mimo, dostane od jádra chybu, umře jako kterýkoli jiný proces a dozorčí proces ho restartuje. Systém běží dál.
Cena je IPC. Operace, která byla v monolitu skokem, je teď meziprocesová komunikace: přechod do jádra, předání zprávy, přepnutí kontextu na obslužný proces a totéž zpátky. Jedno čtení ze souboru překročí hranici čtyřikrát místo jednou a jeden přechod stojí zhruba 50-100 ns, s mitigacemi Spectre a Meltdown i pětinásobek.
Ten argument byl v roce 1992 silnější než dnes: L4 z roku 1993 srazilo cenu zprávy o řád a ukázalo, že pomalý byl Mach, ne myšlenka.
Kde mikrojádro doopravdy vyhrálo
QNX - infotainment a přístrojové štíty v autech, průmyslové řízení, zdravotnická technika. Certifikace podle norem funkční bezpečnosti se na malé důvěryhodné bázi dělá řádově snáz.
seL4 - mikrojádro o zhruba deseti tisících řádcích C se strojově ověřeným důkazem, že implementace odpovídá své specifikaci. Není to důkaz, že postavený systém je bezpečný - jen že jádro dělá právě to, co je ve specifikaci.
MINIX 3 - od generace Skylake (2015) běží varianta MINIXu 3 uvnitř Intel Management Engine, tedy na samostatném procesoru v čipsetu prakticky každého moderního Intelu. O tom jádru přitom skoro nikdo neví a nikdo nad ním nemá kontrolu.
Hybridy: Windows NT a XNU
Windows NT navrhl Dave Cutler jako vrstvený systém s mikrojádrovými rysy: podsystémy prostředí v uživatelském režimu a jasně vymezené vrstvy uvnitř. XNU v macOS je doslova složenina - jádro odvozené z Machu pro správu paměti a zprávy, nad ním BSD vrstva pro procesy, souborové systémy a sokety.
Obojí si vzalo z mikrojádra strukturu a z monolitu adresní prostor. Komponenty jsou oddělené rozhraními, ale běží vedle sebe se stejnými právy. BSD vrstva v XNU s Machem nemluví zprávami, volá funkce.
Proč grafika skončila v jádře i tam
NT 4.0 v roce 1996 přesunul grafický podsystém a správce oken z uživatelského procesu dovnitř jádra, do win32k.sys. Důvod byl jediný: každé vykreslení znamenalo přechody hranice a na tehdejším hardwaru to bylo vidět. Windows tím získalo rychlost a jednu z nejplodnějších oblastí zranitelností ve své historii - chyba v kreslení fontu je najednou chyba v ringu 0.
macOS jde dnes opačným směrem a od verze 10.15 (2019) tlačí ovladače ven přes DriverKit. Kdykoli se ale výkon a izolace potkají na horké cestě, vyhraje výkon.
Tři architektury vedle sebe
| Vlastnost | Monolit | Mikrojádro | Hybrid |
|---|---|---|---|
| Volání mezi částmi | skok, jednotky ns | zpráva, stovky ns až µs | podle vrstvy |
| Pád ovladače | položí stroj | restart služby, běží dál | většinou položí stroj |
| Důvěryhodná báze | miliony řádků | tisíce až desítky tisíc | miliony řádků |
| Ladění | výpis z paniky | debugger na proces | výpis z paniky |
| Kde běží | Linux, BSD, Android | QNX, seL4, Intel ME | Windows NT, macOS |
Z toho plyne, co se v praxi děje: rozhoduje se podle toho, co stojí selhání, ne podle toho, co stojí volání. Na serveru je pád stroje nepříjemnost, v řídicí jednotce brzd jiná kategorie problému.
Exokernel a unikernel: jedna aplikace, jedno jádro
Exokernel z devadesátých let posunul myšlenku opačným směrem: jádro jen bezpečně rozděluje hardware a veškerou abstrakci si aplikace linkuje jako knihovnu. Unikernel je jeho praktický potomek - aplikace a knihovní systém se přeloží do jednoho obrazu, který běží v jediném adresním prostoru přímo nad hypervizorem.
Obraz má jednotky megabajtů, startuje v desítkách milisekund a systémová volání nestojí nic, protože žádná hranice není.
Pro tvůj server to není. Nemáš shell, nemáš ps, nemáš druhý proces, kterým bys to opravil, a chyba v aplikaci má rovnou přístup ke všemu. Smysl to dává na specializované virtuální stroje s tisíci jednoúčelových instancí. Všechno ostatní se dělá kontejnery.
Kam se to posunulo v praxi
Linux je monolit a zůstane jím. Přesto z něj systematicky tlačí věci ven.
FUSE - souborový systém jako obyčejný proces, v jádře od roku 2005. Tak fungují sshfs i ntfs-3g. Cena je několik přechodů hranice na operaci, výhoda je, že chyba v cizím formátu neshodí stroj. Detaily jsou u VFS a montování.
Ovladače v uživatelském prostoru - SPDK a DPDK obcházejí jádro úplně. Rozhraní vfio s pomocí IOMMU předá zařízení procesu, ten si ho ovládá sám a data putují bez jediného systémového volání. Vyplatí se to při milionech paketů za vteřinu, kdy je cena přechodů hlavní položkou rozpočtu. Víc u vstupu a výstupu.
eBPF: cizí kód v jádře, který smí projít jen ověřovatelem
eBPF je nejzajímavější odpověď na celý ten spor. Nahraješ do jádra vlastní program, ale ověřovatel před spuštěním dokáže, že skončí, že nesahá mimo povolenou paměť a že volá jen povolené funkce.
sudo bpftool prog show # jaké programy právě běží v jádře a odkud se tam vzaly
Ochranu tady nedělá hranice adresního prostoru, ale statická kontrola před spuštěním. Potkáš to v nástrojích a diagnostice, aniž bys tušil, že řešíš architekturu jádra.
Je to konvergence, ne kapitulace. Monolit si bere z mikrojádra izolaci tam, kde se vyplatí, a mikrojádrové systémy si berou z monolitu rychlé cesty. Ani jedna strana necouvla, obě jen přestaly platit plnou cenu.
Jeden ovladač, 8,5 milionu strojů
- července 2024 vydal CrowdStrike aktualizaci datového souboru pro svůj bezpečnostní senzor. Ten soubor nebyl program, ale zpracovával ho ovladač běžící v jádře Windows a ten na něm spadl. Následovala modrá obrazovka při každém startu, zhruba 8,5 milionu strojů, zrušené lety a stojící nemocnice.
Poučení je přesně to, o čem je celá tahle stránka: kód uvnitř hranice nemá kam spadnout. Kdyby tatáž chyba potkala proces v uživatelském prostoru, restartoval by se. Podrobnosti patří na jádro a uživatelský režim.
Kde ta volba přestává být tvoje
Architekturu jádra si nevybíráš. Vybírá ji hardware a to, kde existuje ovladač.
Ovladač napíšeš tam, kde ho podporuje výrobce čipu, a to je v praxi jádro. Dokumentace, vzorový kód i rozhraní míří dovnitř. Uživatelský prostor je varianta jen u úzké skupiny zařízení.
Na serveru volba neexistuje. Provozuješ Linux nebo Windows, oba jsou monolit s moduly. Ovlivníš jediné: kolik cizího kódu do jádra pustíš.
Vybírat si smíš u vestavěných zařízení. Řídicí jednotka v autě, zdravotnický přístroj, průmyslový kontrolér - tam se QNX nebo seL4 vyplatí, protože se počítá cena selhání, ne cena volání.
Co si odnést
Rozhoduje hranice ochrany, ne jméno architektury. Uvnitř je všechno rychlé a smrtelné, venku pomalejší a restartovatelné.
Modul není mikrojádro. lsmod ukazuje kód se stejnými právy jako zbytek jádra, jen zavedený za běhu.
Monolit vyhrál tam, kde se počítá propustnost, mikrojádro tam, kde se počítá selhání. Dvě správné odpovědi na dvě různé otázky.
IPC je celá cena mikrojádra a od L4 je řádově nižší, než jak se ten argument pořád opakuje.
Hybridy jsou monolity se strukturou. Oddělené komponenty, jeden adresní prostor, jedna panika.
Linux tlačí věci ven a monolitem být nepřestává. FUSE, vfio, eBPF - konvergence, ne kapitulace.
Kam dál
- Jádro a uživatelský režim - ta hranice, o kterou tu celou dobu jde
- Systémová volání - jak přechod přes hranici vypadá zevnitř
- Meziprocesová komunikace - mechanismy, na kterých mikrojádro stojí
- Vstup a výstup - kde ovladače v obou světech doopravdy sedí
- Virtualizace a kontejnery - druhý způsob, jak izolovat
- Start systému - co znamená panika jádra při startu