Jak funguje initramdisk
Pravděpodobně každý alespoň trošku zkušenější uživatel linuxu se již setkal s pojmem initramdisk. Většinou na něj narazíme při konfiguraci bootloaderu, nebo instalaci nového kernelu. Obvykle ho však bereme jen jako jakýsi blackbox, který není radno měnit, mazat a vůbec nejlepší je předstírat, že si ho ani trochu nevšímáme. Odvážnější uživatelé, nebo ti, kteří k tomu byli donuceni okolnostmi (například pokud provozují exotické řadiče SCSI diskových polí) si již zkoušeli vygenerovat initramdisk vlastní pomocí utility mk_initrd a v lepším případě se jim pak i podařilo jejich stroj opět nabootovat. Víte ale, k čemu initramdisk vlastně ve skutečnosti je, jak funguje a co s ním lze provozovat za kouzla? Pokud vás to zajímá, čtěte dál...
Předpoklady
Abyste mohli využívat initramdisk, je nutné mít zapnutou jeho podporu
v kernelu. V aktuální řadě 2.6.x to znamená při make menuconfig
v menu Device drivers -> Block devices zaškrtnout
volby RAM disk support a
následně se objevivší Initial RAM disk support. Dále v kernelu
musíte mít natvrdo zakompilovanou podporu systému souborů
(filesystem), který na ramdisku použijete - v našem případě to bude
ext2. Většina jader ze známých distribucí by oba tyto
předpoklady měla splňovat, takže nemusíte nic překládat.
Dále potřebujete boot loader, který umí initramdisky nahrávat do
paměti. Vyhovující jsou prakticky všechny v linuxu používané,
například LILO, GRUB či loadlin. Ani pokud vašeho miláčka
bootujete přes síť, tak nejste ztraceni. Jak etherboot, tak
netboot si s iniramdisky poradí.
Posledním důležitým programem bude mke2fs, kterým budoucí
ramdisk naformátujete. Můžete samozřejmě použít i jiný filesystem,
pokud ovšem do jádra zakompilujete jeho podporu.
Pro tvorbu initramdisku je též vhodné mít v kernelu zapnutou volbu
Block devices -> Loopback device support, která vám
umožní se k libovolnému souboru za pomoci /dev/loop*
chovat jako k blokovému zařízení (disku). Budete ho tedy například
moci naformátovat, připojit (mount) a vytvářet v něm soubory.
Trocha teorie
Hlavní důvod, pro který se initramdisk používá, je zpřístupnění kořenového adresáře (root filesystem, rootfs) pro namountování jádrem. V praxi může jít například o situaci kdy je root filesystem na SCSI disku, pro jehož řadič máte ovladač jako modul, případně je na filesystemu s jehož ovladačem jste na tom podobně. To je poměrně prekérní situace, protože aby mohl být natažen modul, je třeba mít přístup k disku, což ovšem není možné bez nahraného modulu.
Řešení nám samozřejmě nabízí initramdisk, který nahrání patřičného
modulu dokáže zajistit.
Jak to tedy v praxi funguje?
- Bootloader (LILO, GRUB, ...) nahraje do paměti jádro Linuxu, které se většinou nachází v souboru jehož jméno začíná na vmlinuz, případně na bzImage.
- Potom bootloader nahraje soubor s initramdiskem. Ten má obvykle jméno initrd.gz, nebo podobné.
- Bootloader teprve teď spustí kernel, ten se rozkomprimuje, zinicializuje všechno co je potřeba a v určitém okamžiku zjistí, že v paměti je připraven initramdisk.
- Pokud je zkomprimován (např. metodou gzip), samozřejmě ho nejprve rozbalí. Nezkomprimovaný ramdisk následně přimountuje jako / (root). Úspěch tohoto kroku je podmíněn přítomností ovladače použitého filesystemu (např. ext2 či minix) v jádře.
- Nyní jádro zkontroluje, jestli nebylo voláno s parametrem root=/dev/ram, případně zda nebyl nastaven root na ramdisk např. pomocí programu rdev.
- Pokud ano, bude tento ramdisk považovat za root filesystem a bude pokračovat bodem 9. To nás ale tolik nezajímá, takže se zaměříme na opačný případ:
- Parametr root=/dev/ram zadán nebyl, ramdisk je tedy initramdisk. V tom případě se jádro podívá, jestli náhodou na ramdisku neleží soubor /linuxrc. Pokud leží, pokusí se ho spustit (s PIDem větším než 1) a počká, než tento proces skončí.
- Nyní jádro vyhodnotí, které zařízení má být použito jako root (prozkoumá parametry root=, nfsroot= a hodnoty zapsané programem rdev) a pokusí se ho přimountovat. Pokud se mu to povede, zjistí jestli na něm existuje adresář /initrd. V kladném případě do něj namountuje použitý initramdisk, v záporném případě uvolní paměť ramdiskem využívanou.
- Na tomto rootu zkusí jádro najít jeden z následujících programů: /sbin/init, /etc/init, /bin/init, /bin/sh (v tomto pořadí), případně program specifikovaný parametrem init=. Pokud se mu to povede, spustí ho s PID=1 a UID=0, čímž odvedlo svou práci a bootování pro něj skončilo. Zbytek bude mít na starosti právě spuštěný proces init, ale to nás už v tomto článku nebude zajímat.
Je zřejmé, že naše pozornost se bude upínat především k bodu 7.
mk_initrd
mk_initrd případně mkinitrd se jmenuje skript po kterém sáhneme v případě, že nás potkala první situace z úvodu článku, tedy že potřebujeme nahrát nějaký modul abychom se vůbec dostali k rootfs a mohli pokračovat v bootování. Tento skript je obvykle ušit "na míru" konkrétní distribuci a kupříkladu v SUSE Linuxu 9.1 (SL91) už je poměrně komplexní a nepřehledný. Nicméně ať už používáte jakoukoliv distribuci, základní princip funkce mk_initrd bude vždy podobný:
- Nejprve mk_initrd si vyrobí soubor o velikosti řekněme
4MB, připojí k němu loopback device, naformátuje ho třeba jako ext2 a
přimountuje. Vytvoří v něm potřebné adresáře, do /sbin překopíruje pár
programů, např.
modprobe
ash
a v/dev
vytvoří něco málo nezbytných zařízení. Aby nebyly potřeba sdílené knihovny a ušetřilo se místo jsou obvykle použity staticky slinkované binárky, ale není to pravidlem. - Dále si mk_initrd musí obstarat seznam modulů, které je
potřeba v initramdisku nahrát. V SL91 je tento seznam v proměnné
INITRD_MODULES
v souboru/etc/sysconfig/kernel
, jiné distribuce můžou používat jiné metody. Obvykle je i možnost zadat jména modulů na příkazové řádce. - Řekněme, že je potřeba nahrát ovladač
aic7xxx.ko
pro SCSI disk a modul filesystémuext3.ko
. Pokud používáte jádro 2.6.8, budou moduly zkopírovány z/lib/modules/2.6.8
na stejné místo v initramdisku. Např. ovladač filesystému skončí jakolib/modules/2.6.8/kernel/fs/ext3/ext3.ko
. Samozřejmě bude potřeba zjistit závislosti a patřičně rozšířit seznam kopírovaných modulů. - Nyní mk_initrd v kořenovém adresáři vznikajícího
initramdisku vytvoří spustitelný soubor
linuxrc
v nejjednodušším případě s následujícím obsahem:#!/bin/sh /sbin/modprobe aic7xxx /sbin/modprobe ext3
Jednoduché, že? Místomodprobe
je někdy použitinsmod
, ale to je detail. - Nyní bude váš nový initramdisk odmountován, odpojen loopback a soubor, kde byl filesystem vytvořen může volitelně být zkomprimován programem gzip.
- Jméno výsledného souboru vám bude oznámeno, případně bude přímo uložen na místo požadované parametrem.
Příklad
V již zmíněném SUSE Linuxu 9.1 bude výroba initramdisku probíhat následovně:
root:~# mk_initrd \ -k /boot/vmlinuz-2.6.8 \ -i /boot/initrd-2.6.8 \ -m "ext3 aic7xxx" Root device: /dev/hda2 (mounted on / as ext3) Module list: ext3 aic7xxx Kernel image: /boot/vmlinuz-2.6.8 Initrd image: /root/initrd.hokus Shared libs: lib/ld-2.3.3.so lib/libc.so.6 Modules: kernel/drivers/scsi/scsi_mod.ko kernel/drivers/scsi/sd_mod.ko kernel/fs/jbd/jbd.ko kernel/fs/ext3/ext3.ko kernel/drivers/scsi/aic7xxx/aic7xxx.ko
Nyní už jen musíte oznámit bootloaderu, že při příštím bootu
chcete zavést tento initramdisk a máte vyhráno. Pokud používáte LILO,
a initramdisk byl vytvořen jako /boot/initrd-2.6.8
, tak
musíte do souboru /etc/lilo.conf
do patřičné sekce přidat řádek
initrd=/boot/initrd-2.6.8
a zavést tuto novou konfiguraci zadáním příkazu lilo
. V případě
GRUBu bude postup obdobný.
To je vše. Váš ramdisk je vytvořen a připraven k použití. Samozřejmě že v mnoha distribucích je vytváření a instalace initramdisku automatická, případně klikací. Ale ono nikdy neškodí vědět jak věci fungují pod pokličkou.
Zkoumáme
Pokud si chcete prohlédnout vnitřnosti svého čerstvého výtvoru, budete ho muset nejprve rozkomprimovat a získaný soubor přimountovat jako loopback device třeba do /mnt:gzip -cd /boot/initrd-2.6.8 > /tmp/initrd.img mount -oloop /tmp/initrd.img /mnt [...] umount /mnt
Modifikujeme
Činnost initramdisku je možné ovlivňovat například pomocí parametrů
předaných kernelu. Všechny parametry nerozpoznané kernelem budou
předány jako proměnné do prostředí procesu linuxrc
. Takže pokud do lilo.conf
přidáte řádku
append='POZDRAV="Ahoj kámo"'
a na konec linuxrc
dopíšete něco jako
echo ======= $POZDRAV =======
pozdraví vás při dalším bootu initramdisk pěkně podle vašeho gusta.
Samozřejmě před rebootem linuxrc
uložíte, initramdisk
odmountujete, image zkomprimujete do /boot/initrd-2.6.8
a
případně spustíte lilo
.
Initramdisk pro pokročilé
Uznávám, že uvedený příklad s pozdravem je celkem hloupý, ale předávání parametrů initramdisku se přesto může hodit. Pokud třeba rootfs máte v souboru na disku s Windows a chcete ho mountovat přes loopback device. Ovšem jak jádru říct, který soubor na které patrition má pro loopback použít? Můžete mu zkusit vnutit root=C:\linux\rootfs.img ale asi tušíte že to nebude ta pravá cesta. Linux totiž o žádném disku C: nemá ani potuchy. Navíc jako hodnotu parametru root= očekává jméno tzv. blokového zařízení, tedy například disku. Takže mu podstrčíme root=/dev/loop0 a můžeme si napsat initramdisk, který bude schopen toto zařízení připojit k souboru C:\linux\rootfs.img. Abychom využili nedávno získaný poznatek o předávání parametrů do prostředí bude ten initramdisk univerzální a budeme mu předávat parametr typu
loopinit=/dev/hda1,vfat,/linux/rootfs.img,/dev/loop0,rw
podle něhož se bude initramdisk ve svém snažení řídit.
Postup jeho činnosti bude následující:
- Zjistí proměnnou prostředí loopinit
- Patřičně jí "rozseká" na jednotlivé části.
- Nahraje moduly pro vfat filesystem.
- Vytvoří adresář /host a přimountuje do něj hostfilesystem /dev/hda1. Jeho typ bude vfat a disk bude přimountován pro čtení i zápis (rw).
- Připojí soubor /host/linux/rootfs.img k zařízení /dev/loop0.
- Skončí :-)
Nepředpokládám, že by podobný initramdisk využilo víc než zanedbatelné množství čtenářů, takže nebudu ztrácet čas konkrétním příkladem.
Závěr
Doufám že se mi povedlo alespoň malinko poodhalit tajemství černé skříňky zvané initramdisk a ukázat možná nečekaná použití. Teď už víte, že základem je vlastně jen obyčejný skript a můžete se směle vrhnout na experimentování.