Ключевые слова:linux, mount, loop, fs, ext2fs, boot, (найти похожие документы)
From: Андрей Рандрианасулу <randrik@mail.ru>
Date: Mon, 28 Feb 2004 14:31:37 +0000 (UTC)
Subject: Использование loopback устройства для монитрования root-раздела в Linux
Оригинал: http://linuxshop.ru/linuxbegin/article636.html
Вся система в одном файле.
Андрей Рандрианасулу <randrik@mail.ru>, 12.12.2003 -
Сижу я однажды после бессонной ночи в интернете, читаю HOWTO. И
попадается мне очень интересная HOWTO-шка под названием
"Loopback-Root-FS (использование loopback устройства Linux для
создания файловой системы Linux, которая может запускаться из
DOS-раздела без изменения системы разделов)" О как. Там идет речь о
создании корневой файловой системы ext2 внутри файла, лежащего на
разделе MS-DOS. И о запуске всей системы с помощью 3 файлов:
загрузчика LOADLIN, ядра и этого файла с ext2 внутри.... Правда, там
еще свап предлагают создать.... Тоже в файле на разделе DOS. Ну
конечно я свап создал ... на 16 метров :) Чисто на всякий пожарный.
Однако по ходу дела возникло много вопросов, в ходе решения которых я
узнал много интересного...
Итак, в начале я создал командой dd все необходимые файлы:
#dd if=/dev/zero of=/root/initrd.img bs=1024 count=4096
(Это - заполненный нулями файл размером в 4 Мб, который вскоре будет
нести в себе файловую систему Ext2 для Initial RAM drive).
#dd if=/dev/zero of=/root/linuxdsk.img bs=1024 count=16384
(Тут будет корневая ФС, размер [count=], разумеется, нужно выбирать
разумно, исходя из того, сколько места есть на диске и сколько места
хочется получить внутри файла когда там будет создана файловая
система... Я "навскидку" решил, что мне для тренировки хватит 16 Мб)
#dd if=/dev/zero of=/root/linuxswp.img bs=1024 count=16384
(Тот самый свап... При наличии 128 Мб ОЗУ и отстутствии Х-ов он может
быть и таким маленьким... Хотя можно создать и больший, если надо.)
Такие маленькие файлы, заполненные нулями, создаются на PII-420 с
быстрым современным HDD очень недолго. Однако при создании вскоре
(интереса для) 5Гб файла мне пришлось пойти попить чайку :)
Теперь, когда файлы созданы, в них создается файловая система (в этом
случае - ext2, потом мне пришлось создавать в основном системном файле
ext3, но об этом позже).
# cd /root
# mke2fs -i 1024 -b 1024 -m 5 -F -v initrd.img
# mke2fs -i 1024 -b 1024 -m 5 -F -v linuxdsk.img
# mkswap linuxswp.img
Значения всех опций можно узнать в man-странице mke2fs
Теперь пора примонтировать созданные файлы:
# mount -o loop /root/linixdsk.img /mnt/disk
(Монтируем как loopback-устройство к каталогу /mnt/disk)
# mount -o loop /root/initrd.img /root/initrd
(Разумеется, я этот каталог сначала создал в /root )
Захожу в каталог /mnt/disk и вижу там папку lost+found :) Сюрприз...
В /root/initrd она тоже есть. Ладно, она же ведь пока пустая и ничем
не мешает :)
Теперь пора наполнить содержимым обе файловые системы - скопировать
туда необходимые файлы и каталоги.
Начать видимо следует с initrd.img, смонтированного у меня к
/root/initrd, т.к. в HOWTO был так описан процесс загрузки всей этой
системы:
---cut on-------
Последовательность загрузки Linux
Чтобы понять, как загрузочный электронный диск работает в процессе
загрузки системы, изучим порядок событий при загрузке системы.
1. Ядро загружается в память. Эту операцию производит LILO или
LOADLIN. В этот момент выводится сообщение Loading...
2. Образ электронного диска загружается в память, это тоже делает LILO
или LOADLIN. В этот момент также выводится сообщение Loading...
3. Производится инициализация ядра, включая обработку опций командной
строки и подключение электронного диска в виде корневой файловой
системы.
4. На загрузочном электронном диске запускается программа /linuxrc.
5. Корневое устройство переключается в соответствии с параметром ядра.
6. Запускается программа /etc/init, которая уже производит
настраиваемую пользователем последовательность загрузки.
Это упрощенное описание того, что происходит на самом деле, но этого
достаточно, чтобы описать, как запускается ядро и как используется
электронный диск.
---cut off----
Ну значит поехали... Там же чуть ниже приведен ПРИМЕР содержимого
загрузочного эл. диска (aka Initial RAM drive)
---cut on----
Содержимое загрузочного электронного диска, которое я использовал,
приведено ниже. Оно заняло примерно 800 Кб, если принимать в расчет
излишки файловой системы.
total 18
drwxr-xr-x 2 root root 1024 Jun 2 13:57 bin
drwxr-xr-x 2 root root 1024 Jun 2 13:47 dev
drwxr-xr-x 2 root root 1024 May 20 07:43 etc
drwxr-xr-x 2 root root 1024 May 27 07:57 lib
-rwxr-xr-x 1 root root 964 Jun 3 08:47 linuxrc
drwxr-xr-x 2 root root 12288 May 27 08:08 lost+found
drwxr-xr-x 2 root root 1024 Jun 2 14:16 mnt
./bin:
total 168
-rwxr-xr-x 1 root root 60880 May 27 07:56 ash
-rwxr-xr-x 1 root root 5484 May 27 07:56 losetup
-rwsr-xr-x 1 root root 28216 May 27 07:56 mount
lrwxrwxrwx 1 root root 3 May 27 08:08 sh -> ash
./dev:
total 0
brw-r--r-- 1 root root 3, 0 May 20 07:43 hda
brw-r--r-- 1 root root 3, 1 May 20 07:43 hda1
brw-r--r-- 1 root root 3, 2 Jun 2 13:46 hda2
brw-r--r-- 1 root root 3, 3 Jun 2 13:46 hda3
brw-r--r-- 1 root root 7, 0 May 20 07:43 loop0
brw-r--r-- 1 root root 7, 1 Jun 2 13:47 loop1
crw-r--r-- 1 root root 1, 3 May 20 07:42 null
crw-r--r-- 1 root root 5, 0 May 20 07:43 tty
crw-r--r-- 1 root root 4, 1 May 20 07:43 tty1
crw-r--r-- 1 root root 1, 5 May 20 07:42 zero
./etc:
total 3
-rw-r--r-- 1 root root 2539 May 20 07:43 ld.so.cache
./lib:
total 649
lrwxrwxrwx 1 root root 18 May 27 08:08 ld-linux.so.1 ->
ld-linux.so.1.7.14
-rwxr-xr-x 1 root root 21367 May 20 07:44 ld-linux.so.1.7.14
lrwxrwxrwx 1 root root 14 May 27 08:08 libc.so.5 -> libc.so.5.3.12
-rwxr-xr-x 1 root root 583795 May 20 07:44 libc.so.5.3.12
./lost+found:
total 0
./mnt:
total 0
---cut off----
Приняв это за основу, я скопировал актуальные для моей системы версии
файлов.... Опа... Со времени написания HOWTO прошло почти 4 года, и
"начинка" заняла почти 2.5 мега... Разумеется, с этим можно бороться,
призвав на помощь к примеру "The Linux Bootdisk HOWTO" раздел
"Уменьшение размера корневой файловой системы" (для нас это будет
размер первоначальной корневой системы, т.к. корневой впоследствии
станет другая, еще пока даже не заполненая система в файле
linuxdsk.img) Кстати, Bootdisk HOWTO мне очень пригодилось, когда я
создавал основную структуру каталогов и файлов в linuxdsk.img ....
Сколько я нового и интересного узнал о старте Linux-системы... Но это
позже, т.к. в начале я читал не очень внимательно (за что поплатился
чуть позже).
В общем, я призвал на помощь mc и копировал, копировал, копировал....
Чтобы узнать, кому какие библиотеки нужны, я вызывал команду ldd и
получал список требуемых библиотек и их расположение.
Я даже не забыл создать внутри будущей корневой ФС файлик /etc/fstab :
#cat /mnt/disk/etc/fstab
/dev/loop0 / ext2 defaults 1 1
/dev/loop1 swap swap defaults 1 1
Уф. Через некоторое время я, кажется, всё упаковал и в /root/initrd, и
в/mnt/disk (т.е. в файлы /root/initrd.img и /root/linuxdsk.img,
подключенные сейчас туда). Пора писать скрипт linuxrc и готовить ядро
...
Черт! Ядро... Срочно прочитав секцию "Создаём ядро Linux"
Loopback-Root-FS HOWTO, я понял, что при очередной недавней компиляции
ядра я забыл вкомпилировать в него поддержку loopback FS. Она была
(иначе бы опция -o loop команды mount не сработала), но в виде
модуля... И еще я прочитал о необходимости патчить ядро, для
обеспечения возможности загрузки с loopback-устройства. Правда там
самое новое из упомянутых ядер серии 2.3.х у меня же 2.4.21 с патчем
для поддержки xfs. Полез в свой /usr/src/linux и начал там поиск...
Оказалось что со времен серии 2.2.х некоторые описания структур ядра
"переехали" - я, например, нашел список устройств для загрузки в
usr/src/linux/init/do_mounts.c :
---cut on---
static struct dev_name_struct {
const char *name;
const int num;
} root_dev_names[] __initdata = {
{ "nfs", 0x00ff },
{ "hda", 0x0300 },
{ "hdb", 0x0340 },
{ "loop", 0x0700 },
{ "hdc", 0x1600 },
{ "hdd", 0x1640 },
{ "hde", 0x2100 },
{ "hdf", 0x2140 },
{ "hdg", 0x2200 },
{ "hdh", 0x2240 },
{ "hdi", 0x3800 },
{ "hdj", 0x3840 },
{ "hdk", 0x3900 },
{ "hdl", 0x3940 },
{ "hdm", 0x5800 },
{ "hdn", 0x5840 },
{ "hdo", 0x5900 },
---cut off---
Там очень длинная структура, желающие могут ознакомиться....
А в HOWTO рекомендовалось делать так
---cut on----
Для ядер версий 2.0.x в файл /init/main.c надо добавить одну строку, в
соответствии с уже измененной версией, приведенной ниже. В строке,
которую надо добавить написано "loop", 0x0700.
static void parse_root_dev(char * line)
{
int base = 0;
static struct dev_name_struct {
const char *name;
const int num;
} devices[] = {
{ "nfs", 0x00ff },
{ "loop", 0x0700 },
{ "hda", 0x0300 },
...
{ "sonycd", 0x1800 },
{ NULL, 0 }
};
...
}
Для ядер версий 2.2.x в файл /init/main.c надо добавить три строки, в
соответствии с уже измененной версией, приведенной ниже. Надо добавить
строку, в которой написано "loop", 0x0700, а также предшествующую и
следующую за ней:
static struct dev_name_struct {
const char *name;
const int num;
} root_dev_names[] __initdata = {
#ifdef CONFIG_ROOT_NFS
{ "nfs", 0x00ff },
#endif
#ifdef CONFIG_BLK_DEV_LOOP
{ "loop", 0x0700 },
#endif
#ifdef CONFIG_BLK_DEV_IDE
{ "hda", 0x0300 },
...
{ "ddv", DDV_MAJOR /kernel/fs/ [не исключаю, что модули можно и кучей
свалить в /lib/modules, только зачем создавать бардак?]. Правда,
модуль loop.o лежит среди блочных {block} устройств, но ведь никто не
мешает засунуть в InitialRAM drive все, что нужно для монтироания
корневой ФС..... К прмеру драйверы USB для монтирования корня с
флешки....
Скопировал из своей действующей системы все нужные модули в соотв.
каталог /root/initrd . Там у меня до сих пор болтался примонтированный
файл /root/initrd.img. Теперь принимаемся за скрипт /linuxrc (тут
корень уже для загрузившегося с этим RAM-диском ядра...).
В HOWTO был приведен пример:
---cut on---
Файл /linuxrc
Файл/linuxrc на загрузочном электронном диске нужен для того, чтобы
произвести все приготовления, необходимые для подключения зацикленного
устройства как корневого.
Скрипт, приведенный ниже пытается подключить /dev/hda1 как раздел
msdos, и если это происходит удачно, то он настраивает файлы
/linux/linuxdsk.img как /dev/loop0 и /linux/linuxswp.img как
/dev/loop1.
#!/bin/sh
echo INITRD: Trying to mount /dev/hda1 as msdos
# echo INITRD: Попытка подключить /dev/hda1 как msdos
if /bin/mount -n -t msdos /dev/hda1 /mnt; then
echo INITRD: Mounted OK
# echo INITRD: Подключение успешно
/bin/losetup /dev/loop0 /mnt/linux/linuxdsk.img
/bin/losetup /dev/loop1 /mnt/linux/linuxswp.img
exit 0
else
echo INITRD: Mount failed
# echo INITRD: Подключение не удалось
exit 1
fi
Первое устройство /dev/loop0 станет корневым, а второе - /dev/loop1 -
станет swap-пространством.
----cut off----
Я немного подредактировал скрипт под свою таблицу разделов (я решил
поместить файлы linuxdsk.img и linuxswp.img в каталог /linux-loop на
корневом в текущий момент устройстве /dev/hda5). Исправить тип в
команде mount минутное дело. Очень быстро скрипт был надлежащим
образом записан, проверен, закомментирован русским языком :).
Теперь отмонтируем примонтированные устройства
# umount /mnt/disk
# umount /root/initrd
Теперь можно сжать образ загрузочного RAM-диска.
#cd root
#gzip -c -9 initrd.img > initrdgz.img
(все уже поняли, что файлы могут называться как угодно и подключаться
куда угодно, однако файл RAM-диска, похоже, должен иметь имя
initrd*.img, что легко получить, скопировав его в /boot с нужным
именем. Да, стоит внимательно посмотреть, что там еще в /boot есть...
И вообще, работать под рутом надо аккуратно.... Однажды в ходе этой
(многочасовой) эпопеи я чуть не остался без всех своих модулей в
/lib/modules/ - просто вместо F5 в mc нажал F6. Хорошо, опомнился
вовремя.)
Теперь осталось подготовить все для загрузки свеженабранной "с бору по
сосенке" :) Linux-системы.
linuxdsk.img идет в /linux-loop, туда же отправляется linuxswp.img
Ядро у меня уже лежит в /boot (грузить вся я буду с помощью lilo)
Кладем туда же в /boot наш initrdgz.img
Модифицируем /etc/lilo.conf, добавляя туда секцию вида:
---cut on ---
image=/boot/bzImage-ran0
label=loop-i586
initrd=/boot/initrdgz.img
vga=normal
append="root=/dev/loop0"
read-only
---cut off---
ПРЕДУПРЕЖДЕНИЕ!!! Тут я совершил ошибку, видимо... Слепо скопировал
"как написано было". Ядро, видимо, получило сначала команду
append="root=/dev/loop0", и молча проигнорировало наличие RAM-диска,
вывалившись в итоге с kernel panic по поводу невозможности
смонтировать корневую ФС. Полез читать
/usr/src/linux/Documentation/initrd.txt и man initrd. Понял, что
существуют два механизма смены корневой ФС в процессе работы. Мне
удалось реализовать только более старый вариант, через proc
filesystem.
Выдержка из man initrd:
---cut on----
СМЕНА НОРМАЛЬНОЙ КОРНЕВОЙ ФАЙЛОВОЙ СИСТЕМЫ
По умолчанию, конфигурация ядра (например, установленная в файле ядра
с помощью rdev или на этапе компиляции ядра) или установки загрузчика
используются определения нормальной корневой файловой системы.
Для NFS-монтируемой нормальной корневой файловой системы нужно
использовать загрузочные опции nfs_root_name и nfs_root_addrs для
задания установок NFS. За дополнительной информацией по
NSF-монтируемой корневой ФС обращайтесь к файлу документации к ядру
nfsroot.txt. Также дополнительная информация по установкам корневой
файловой системы содержится в документации к LILO и LOADLIN.
Кроме того, /linuxrc может сменить нормальное корневое устройство. Для
того, чтобы /linuxrc сменил это устройство, должен быть подмонтирован
каталог /proc. После монтирования /proc, /linuxrc сменяет нормальное
корневое устройство, записывая установки в файлы proc
/proc/sys/kernel/real-root-dev, /proc/sys/kernel/nfs-root-name, и
/proc/sys/kernel/nfs-root-addrs. Что касается физического корневого
устройства, оно меняется, если /linuxrc записывает номер нового
устройства корневой файловой системы в /proc/sys/kernel/real-root-dev.
Для корневой файловой системы NSF корневое устройство меняется, если
/linuxrc записывает установки NSF в файлы
/proc/sys/kernel/nfs-root-name и /proc/sys/kernel/nfs-root-addrs, а
затем записывает 0xff (например,номер псевдо-NFS-устройства) в файл
/proc/sys/kernel/real-root-dev.
Так, например, следующие команды изменят нормальное корневое
устройство на /dev/hdb1:
echo 0x365 >/proc/sys/kernel/real-root-dev
В качестве примера для NSF, следующие команды изменят нормальное
корневое устройство на каталог NSF /var/nfsroot на локальном сетевом
NSF-сервере с IP-адресом 193.8.232.7 для системы с IP-адресом
193.8.232.7 и именем 'idefix':
echo /var/nfsroot >/proc/sys/kernel/nfs-root-name
echo 193.8.232.2:193.8.232.7::255.255.255.0:idefix
>/proc/sys/kernel/nfs-root-addrs
echo 255 >/proc/sys/kernel/real-root-dev
---cut off---
Мне оставалось только догадаться, что в моем случае для /dev/loop0
нужно написать строчку вида echo 0x700 >/proc/sys/kernel/real-root-dev.
Я прописал монтирование /proc и эту строку в /linuxrc, убрал в
lilo.conf строчку 'append' и система загрузилась.... Почти. Дальше у
меня была долгая пляска вокруг init, однако мне так хотелось спать,
что я решил оставить все разборки с init на завтра.... Да и этот текст
не стоит IMHO делать очень длинным...Насчет init я как-нибудь попозже
напишу.
Я пользовался следующими документами:
1. Bootdisk-HOWTO (как создать загрузочную дискету под Линукс)
2. Loopback-Root-FS (использование loopback устройства Linux, для
создания файловой системы Linux, которая может запускаться из
DOS-раздела без изменения системы разделов)
3. Документация ядра 2.4.21
4. man initrd