Published on 2004-8-18 at root.cz

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?

  1. 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.
  2. Potom bootloader nahraje soubor s initramdiskem. Ten má obvykle jméno initrd.gz, nebo podobné.
  3. 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.
  4. 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.
  5. 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.
  6. 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:
  7. 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čí.
  8. 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.
  9. 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ý:

  1. 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 a sh 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.
  2. 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.
  3. Řekněme, že je potřeba nahrát ovladač aic7xxx.ko pro SCSI disk a modul filesystému ext3.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čí jako lib/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ů.
  4. 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ísto modprobe je někdy použit insmod, ale to je detail.
  5. 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.
  6. 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í:

  1. Zjistí proměnnou prostředí loopinit
  2. Patřičně jí "rozseká" na jednotlivé části.
  3. Nahraje moduly pro vfat filesystem.
  4. 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).
  5. Připojí soubor /host/linux/rootfs.img k zařízení /dev/loop0.
  6. 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í.

Place for your feedback...
20th October 2004 at 12:05
Jak to funguje v praxi?
Dobry den,
Urcite to funguje tak jak jse to popsal v bodech 1-9? Navod v source kernelu (initrd.txt) pocita automaticky s parametrem pro bootloader root=/dev/ram. S jakym parametrem reknu jadru ze ma namountovat root s initrd? Protoze bod 5-7 nefunguje.
Oct 20   12:05 Jak to funguje v praxi? (by A.Korec)
Oct 27   16:43 Re: Jak to funguje v praxi? (by Michal Ludvig)
May 25   0:33 pbliuhzf gladn (by sqhcf mxbk)
Apr 1   12:46 Re: Jak to funguje v praxi? (by anonymous)
Aug 18   13:07 init? (by m4r3k)