Vysvětlení pojmu InitramFS

RDa

  • *****
  • 2 617
    • Zobrazit profil
    • E-mail
Re:Vysvětlení pojmu InitramFS
« Odpověď #15 kdy: 23. 11. 2018, 12:21:13 »
Tak pokial som vas aspon trosku pochopil, tak pri boote, boot loader nacita kernel spolu s initramfs obrazom(ktory je obohateny o zavadzace, jadrove moduly) do pamate ram a nasledne spusti kernel. Kernel skontroluje pritomnost initramfs a ak ho najde, moutne ako / (uz priamo na disk alebo este len do ramdsiku(myslim tym ten ramfs/tmpfs)?) a nasledne spusti /sbin/init, ktory pusta init scripty?

Len bohuzial, stale nerozumiem pojmu 'predom priraveny obraz zavadzacieho systemu na disk'. Stale som nejako v tom, ze ovladace, moduli su priamo v jadre... Preto odpadava nutnosti instalovat ovladace oproti win... A aj ked nahravam nejake moduli do jadra, napr. conntrack modul, nenahravam ho do initramfs...

Chapes to spravne. Ovladace muzou byt:
1. bud v jadre (soucasti linuxu)
2. nebo out of tree (ovladace tretich stran, typicky priklad: nvidia)
3. pripadne prasacke reseni "nas ovladac je patchem pro jadro verze x.y.z", ktere se uz dnes vidi vyjimecne

Pro body 1 a 3 plati, ze to lze kompilovat jako soucast jadra (compiled-in) nebo jako modul, ktery je v extra souboru. U bodu 2 je to vzdy modul.

Situace pro moduly a initramfs vznikla historicky v dobe, kdy pocitace nemeli moc pameti (rozumej - v dobe 8 az 64 MB). Protoze kod kernelu je v pameti udrzovan vzdy (neni odswapovatelny), tak treba nekolik MB ovladacu, ktere tvuj stroj nevyzaduje znamenalo znacne plytvani tou uz tak malou pameti. Byt initramfs obsahuje tech 10MB, jsou to jen "moznosti" - ze kterych se nakonec pouzivala pro konkretni stroj jen jedna. Pokud mel skript v initramfs detekci hw tak dokazal natahnout konkretni modul, ale casteji se tupe natahlo vsechno a doufalo ze neco zpristupni disk s rootfs. Existence modulu ale umoznovala pozdeji udelat rmmod na ovladace, ktere pouzity nebyly a tak uvolnit pamet. Dnes, v dobe NVMe a AHCI mate tyto dva ovladace rovnou v jadre, tenkrat to byly desitky ruznych ovladacu.

Initramfs tedy resi problem slepice a vejce - na uvolneni pameti skrze rmmod jste ovladace diskovych radicu potreboval mit jako moduly, ale kdyz byly jako moduly, tak uz nebyly soucasti kernelu a nedostal by jste se na rootfs.


Re:Vysvětlení pojmu InitramFS
« Odpověď #16 kdy: 24. 11. 2018, 00:12:57 »
Ravise a RDa už se toho ujali, takže teď budu trochu nosit dříví do lesa...

a nasledne spusti /sbin/init, ktory pusta init scripty?

/sbin/init býval původně kdysi dávno jenom jeden - ten co se spouštěl z přímo mountnutého definitivního kořenového svazku. A máte pravdu, že spuštění initu (procesu s PID=1) skutečně následuje vzápětí po mountnutí rootu.

/sbin/init je především spustitelný program. Díky kernelové autodetekci, o jaký typ spustitelného programu se jedná, nemusí jít nutně o ELFový binár, může to být stejně tak nějaký skript, třeba shellový. Pokud se jedná o skript, správný interpreter se natáhne podle konvence #!/cesta/k/interpreteru na prvním řádku skriptu. Ten "hash a vykřičník" jsou skutečně vypečená finta, a právě na ně se jádro chytá. Pokud se nepletu, tak initial ramdisky takový skript pojmenovaný /sbin/init dříve obsahovaly. Jinak když si zkusíte poskládat bootovatelné "distro" skutečně od nuly, tak první školní příklad je ten, že na budoucí kořenový svazek nakopírujete všechny potřebné knihovny, především libc, a minimální sadu systémových prográmků, především shell, a jméno /sbin/init dáte symlinku na /bin/sh nebo tak něco. (Jasně - nebo na Váš skript.)

V situaci, kdy se mountne na / napřed dočasný initramfs, a až pak finální root, je samozřejmě otázkou, jestli bude /sbin/init jeden, nebo dva... V jednom odkazu co jsem vložil do té předchozí tapety je bootování skrz initrd popsáno včetně toho, že k remountu na finální kořenový svazek se používá syscall pivot_root()... čili někde kolem se zřejmě taky přepřáhne dočasný "init z initial ramdisku" na finální init. Pokud to máte takto zařízeno standardním způsobem. Alternativně by asi šlo, aby ten lehký init nebo init skript, co nastartoval z initrd, spustil "finální velký init" nějak explicitně. Vlastně by na to stačil jediný exec() a finální init by převzal i charakteristický PID=1. Nebo by ten velký init mohl nakonec startovat už přímo z initial RAMdiku? Teoreticky ano, ale tušímže to žádná mainstreamová distribuce takto nedělá... Chcete-li vědět, jak je to doopravdy, klidně se ponořte do zdrojáků své distribuce :-)

Koukám tady v Debianu 9 na /sbin/init jednak na finálním rootu (diskový oddíl),
jednak v initial RAMdisku. V obou "svazcích" skutečně takový soubor existuje.
Na disku se jedná o symlink na systemd, uvnitř initrd se jedná zřejmě o hardlink
(jeden z mnoha) na multifukční ELFový binár busyboxu. Takže nejen Gentoo má v initrd zapečený poloplnohodnotný busybox ;-)

Jo a pokud jde o to, co "init" vlastně dělá: ten skript nebo minimální "přípravátor" v initial RAMdisku skutečně hlavně provede pár skriptů (nějaké ty přípravné práce). Pokud jde o "finální velký init", tak tradiční sysv init volal napřed rc.sysinit, pak snad rc.local, a pak všechny startovací skripty různých služeb/démonů, kteří v systému běží. Systemd má konfiguraci trochu jinou a taky o jeho celkové koncepci by se dalo dlouze debatovat, ale v podstatě se opět jedná především o "service manager".

Len bohuzial, stale nerozumiem pojmu 'predom priraveny obraz zavadzacieho systemu na disk'. Stale som nejako v tom, ze ovladace, moduli su priamo v jadre... Preto odpadava nutnosti instalovat ovladace oproti win... A aj ked nahravam nejake moduli do jadra, napr. conntrack modul, nenahravam ho do initramfs...

Smíchal jste několik rovin :-)

Linux má skutečně pro spoustu hardwaru "ovladače" v tom smyslu, že jsou přítomny v upstream vanilkovém stromě zdrojových kódů (textů, programů v jazyce C) a Linus a jeho pobočníci tyto ovladače dlouhodobě oprašují/udržují kompatibilní s "kernel core" tzn. se základem jádra.

Prvním krokem při kompilaci jádra ze zdrojáků je to, že vlezete např. do "make menuconfig", kde u jednotlivých ovladačů zvolíte, zda se mají přilinkovat k hlavnímu bináru jádra (tzv. "monoliticky") nebo zda se mají slinkovat do podoby externích souborů s příponou .ko, těm se říká "jaderné moduly". Je to obdoba dynamických knihoven v user space. Třetí možnost v menuconfigu je, ovladač z buildu zcela vynechat.

Bývaly kdysi dávno doby, kdy hotové binární jádro (výsledek kompilace) tvořil jediný monolit. Ovladače ke kompilaci se daly jenom povolit/zakázat. Pokud povolit, tak to automaticky znamenalo přilinkování do zmíněného monolitu. V zájmu modularity binárních distribucí (bez potřeby kompilovat jádro pro každý stroj zvlášť) byly poměrně záhy zavedeny "moduly": malé úlomky, které se k monolitu daly za jízdy podle potřeby připojovat (a odpojovat).

Základní binární "monolit" jádra je na disku uložen v /boot/vmlinuz-blablabla-verze. Odtud ho při startu sosá bootloader. Moduly .ko na disku spinkají uložené v /lib/modules/verze/... . Do obrazu initial RAMdisku se nakopírují pouze moduly, které jsou před mountnutím finálního rootu potřeba - což nejsou zdaleka všechny. V dnešních univerzálních desktopových distribucích bývá "distribuční" kernel zkompilovaný prakticky se všemi dostupnými ovladači v modulární podobě (.ko) takže příslušný podadresář v /lib/modules/verze je pěkně košatý a nafouklý. Natáhnout modul z disku do jádra (tzn. vlastně ho "spustit") je možno rukama z příkazového řádku příkazem insmod nebo modprobe. Modprobe je schopen automaticky dotahat všechny moduly, na kterých požadovaný modul závisí (systém má k dispozici mapu vzájemných závislostí). Dalšími příkazy z téhle rodiny jsou lsmod a rmmod.

Pod linuxem soubor .ko je něco jako pod Windows soubor .sys. V obou případech se jedná o kus binárního kódu, který lze zavést do běžícího kernelu, a modul/ovladač se definovaným způsobem uvnitř kernelu přihlásí / zapojí do práce. Že se pod Windows soubory .SYS distribuují pohromadě s popisným souborem .INF a podpisem .CAT, a na rozdíl od Linuxu nejsou příliš jemně verzované, to je v zásadě malá nuance, která souvisí s modelem vývoje softwaru (každý operační systém ho má postavený trochu jinak, zcela jistě Linux vs. Windows).

Co je to ovladač: je to malé bago spustitelného kódu, s nějakou standardizovanou "hlavičkou" / datovou strukturou s předepsanými údaji a odkazy na výkonné funkce modulu. Je to vlastně kolekce volatelných funkcí. Některé ty funkce jsou "zveřejněné", tvoří rozhraní modulu vůči zbytku jádra - jiné jsou "privátní", modul je používá pouze interně. Pokud někdy uslyšíte o "tabulce symbolů", tak se jedná o jména funkcí a proměnných uvnitř modulu (především těch veřejných). Velikou tabulku symbolů obsahuje také hlavní monolit jádra. Vzájemné vypořádání symbolů poskytovaných a požadovaných mezi monolitem a moduly navzájem je náplní práce kernelového dynamického linkeru... (Velmi podobně to funguje u user space programů a dynamických knihoven. Ovšem zatímco user-space program při nevyřešených zavislostech zhavaruje, kernelový linker jenom odmítne natáhnout modul, pro který nemohl vyřešit všechny závislosti.)

Na úrovni zdrojových kódů je "ovladač" opět kolekce funkcí - ve zdrojákách. K vytvoření jednoho binárního kernelového modulu může být potřeba jeden nebo více zdrojáků = textových souborů s příponou .c. Předpis pro sestavení modulu z jednoho či více zdrojáků je obsažen v tzv. Makefile. Přesněji zrovna v kernelu se jedná o celý rozsáhlý strom dílčích souborů Makefile a Kconfig (oba jsou to textové soubory) - granularitu skladby (linkování modulů) lze detailně ovlivnit konfigurací kernelu.
Obvykle je granularita kernelových modulů volená tak, že jeden modul = jeden ovladač, pro specifický hardware. Ale klidně může jeden modul obsahovat ovladačů několik. Kromě toho ne všechny moduly obsahují drivery pro hardware, často modul opouzdřuje nějakou čistě softwarovou fičuru v systému. Třeba nějaké dílčí "porovnávací pravidlo" pro netfilter.
Přesto pojmy "modul" a "ovladač" v obecné mluvě často dost splývají.

Ovladače od třetích stran mají v Linuxu těžký život, hlavně kvůli jemnému verzování kernelů. To souvisí s průběžným inkrementárním vývojem a releasováním linuxového kernelu jakožto softwaru. Pod Windows obvykle stačí, aby byl driver kompatibilní s poměrně hrubým základním číslem verze kernelu (cca na úrovni verzí Windows). Pod Windows dokonce existují mechanismy (styl programování), jak zařídit, že může být binárka ovladače kompatibilní s více verzemi Windows.
Zato pod Linuxem je prakticky vynuceno, že zavést do jádra lze pouze modul zkompilovaný proti téže třímístné verzi kernelu (resp. včetně "patchlevel ocásku verze", který si můžete sami definovat při kompilaci), ba dokonce lze zapnout verzování až na úrovni "sériového čísla buildu". Dnešní distribuce toto mají běžně v distibučním kernelu zapnuto, já to ve svých custom kernelech vypínám, abych při ladění modulů nemusel po rekompilaci hlavního kernelu rekompilovat bez pardonu taky všechny externí (out of tree) ovladače.

Ovladač, od kterého máte k dispozici kompletní zdrojáky, můžete poměrně snadno přikopírovat a zaháčkovat do vanilkového stromu, který jste si stáhl na lokální disk - a zkompilovat "in tree". Existuje ale také oficiálně podporovaný postup, jak svůj custom ovladač zkompilovat "out of tree", pouze s odkazem na hlavní strom někde jinde na disku (a jeho verzování). Rozdíl je nakonec vlastně jenom v cestě k adresáři, kde se zdrojáky "vašeho" modulu nacházejí. Čili toto platí pro "ovladače třetích stran", pokud jsou k dispozici kompletní zdrojáky.

Jiná je situace u ovladačů třetích stran, které obsahují "binární blob". Překlad mnoha tradičních programovacích jazyků do binárního executable kódu se děje ve dvou krocích: kompilace a linkování. První krok vyrobí z jednotlivého zdrojového souboru (např. .c) "polotovar" zvaný "objektový soubor" (.o nebo třeba .obj). Ve druhém kroku se několik objektových souborů slinkuje do potřebného finálního formátu: to může být user-space executable binárka, nebo statická či dynamická knihovna, nebo třeba kernelový modul. Výrobci hardwaru mají i pod Linuxem sklon, nedávat volně k dispozici zdrojové kódy ovladačů. Nechtějí podporovat konkurenci apod. Takže dodávají nějakou interní knihovnu ovladače v podobě "objektového polotovaru". A to je právě ten ohavný binární blob. On obsahuje tabulku symbolů, takže jeho funkce lze volat - ale zároveň je to černá skříňka, do jejíž vnitřní logiky nikdo nevidí. A může se stát, že ten blob byl zkompilovaný proti starší verzi kernelu (jde o deklarace structů a prototypy funkcí, ale taky o věci hlouběji pod kapotou), takže když ho jenom obalíte do open-source "košilky" a tu zkompilujete proti kýženému novějšímu kernelu, tak to vypadá navenek OK, ale ten blob může za provozu způsobovat chyby. Což je vedle ideologického imperativu dost dobrý praktický důvod, proč se binárním blobům v ovladačích bránit. A že se Linux (Linus) brání zuby nehty.

Končím - chtěl jsem něco vysvětlit a spíš do toho jenom dál a dál zabředávám...