Мне нравится, когда компьютеры мало шумят: с тихим компьютером работать
приятнее. Шум, который издает компьютер, а точнее, его отсутствие,
весьма важно, когда комьютер выполняет роль домашнего кинотеатра.
Шумящий компьютер отвлекает и особенно будет заметен в тихих сценах.
В идеальном случае, конечно, не должно быть движущихся частей:
детали, которые двигаются или вращаются непременно трутся, что создает
шумы. Я поставил целью убрать все то, что вращается и движется в моем
компьютере: у меня стоит процессор, которому не нужен вентилятор на
радиаторе, а также бесшумный блок питания. Однако осталась одна вещь,
которая все еще издает звуки - это жесткий диск, на котором установлен
Linux. Если его убрать, моя система станет по-настоящему тихой, и я
взялся за поиски решения.
Единственный практический выбор невращающегося носителя для
установки системы - это USB Flash. Большинство BIOS поддерживают
возможность загрузки с Flash-устройства, а других легкодоступных
устройств с требуемыми габаритами нет. Поэтому выбор был очевиден:
нужно скопировать операционную систему с жесткого диска на
Flash-носитель и загрузить ее.
К сожалению, это не так просто. Чтобы BIOS распознал
Flash-носитель и загрузил с него компьютер, он должен быть
отформатирован в простом формате старого доброго FAT32. Но FAT32
неприменим для хранения корневой файловой системы Linux, поэтому нужно
проделать несколько шагов:
-
Сжать корневую файловую систему в образ диска
-
Загрузить ядро Linux с FAT-форматированного Flash-носителя
-
Заставить ядро смонтировать сжатый образ
-
Продолжить загрузку с этого образа
Что нам понадобится
На каждом шаге будут нужны разные средства,
более конкретно о них будет написано в соответствующих шагах. Вот
краткий список того, что будет использовано:
-
Инструменты SquashFS. Будем использовать SquashFS для сжатия
корневой файловой системы в образ, для этого потребуется средство
создания файловой системы SquashFS.
-
SysLinux. Заставить Linux загружаться с FAT-диска - это подвиг, если только вы не использовали Syslinux; многие вещи становятся проще.
-
BusyBox. Чтобы заставить Linux загрузить образ файловой системы,
нужно смонтировать, перейти в каталог и выполнить pivot_root (детали
опущены). BusyBox предоставляет все это, объяснения немного позже.
Шаг 1а: Копируем существующую файловую систему
Первый шаг -
создать образ диска, который в итоге должен стать корневой файловой
системой. Наверняка вы захотите создать образ с текущей запущенной
системы: у меня это был медиа-сервер, на котором запущен Gentoo. Нельзя
сжимать / как есть, так как в данный момент могут быть примонтированы
другие файловые системы. Поэтому нужно убрать эти монтирования. Простой
способ - перемонтировать / где-нибудь в другом месте:
Бинд-монтирование / для устранения точек монтирования
# mkdir /root/bindmount
# mount -o bind / /root/bindmount
Новая бинд-монтированная корневая система станет чистым
представлением того, что располагается на диске, без лишних на данный
момент смонтированных файловых систем: это означает, что не должно быть
файлов в каталогах /proc или /sys, и небольшое число файлов в /dev.
Если все же остались какие-то файлы в этих каталогах, вы можете смело
их удалить:
Очистка каталогов монтирования
# cd /root/bindmount
# rm -rf proc/* dev/* sys/*
# cp -a /dev/console /dev/null /dev/initctl dev
Последняя строка копирует три устройства, которые необходимы в
процессе загрузки: символьные устройства console и null, а также
FIFO-пайп, используемый системным процессом init. Другие файлы
устройств создаются автоматически ядром.
Не забывайте, в /root/bindmount мы все еще работаем с самой
корневой файловой системой; нужно сделать копию, с которой будем
производить остальные действия:
Копируем bindmount
# mkdir /root/fscopy
# cp -av /root/bindmount/* /root/fscopy
После завершения копирования можно спокойно удалять все файлы в
каталоге tmp и var/tmp (в копии!), так как это временные файлы и больше
не нужны.
Шаг 1б: Сжимаем корневую файловую систему
В идеальном мире
можно было бы взять и записать его на Flash, с которого бы загружалась
система. К сожалению, есть две причины, почему это не так просто:
FAT32 Как ранее упоминалось, файловая система, реализованная
во Flash-носителях - это FAT, которая непригодна для хранения корневой
файловой системы Linux. Ладно, мы просто поместим несжатый образ диска
на носитель и будем читать и писать с него. Тут мы сталкиваемся со
второй проблемой;
Ограничение числа записей Flash-память имеет ограничение по
числу перезаписей, после достижения которого биты станут "липкими" и
данные будут портиться. Правда, это число с шестью нулями, но я хочу
сделать такую систему, чтобы она оставалась работоспособной не 1-2
года, а, предположим, десятки лет. Поэтому, нужен способ хранения
файловой системы так, чтобы ее изменение не сопровождалось записью на
Flash.
К счастью, умные люди уже придумали такой способ: держим сжатый
образ на Flash лишь в качестве отправной точки файловой системы, а
изменения храним RAM-диске в оперативной памяти. Создадим RAM-диск
позже, но образ диска может быть создан прямо сейчас, с использованием
SquashFS.
Есть два достоинства SquashFS. Во-первых, его высокий
коэффициент сжатия; файловая система в 2Гб на моем медиа-сервере
сжалась до 500 Мб, что идеально для Flash-носителей малого объема.
Во-вторых, чтение производится блоками (по умолчанию размер блока 64
Кб) и ядро кэширует лишь небольшое число блоков в данный момент; как
результат, SquashFS занимает малый объем памяти.
Создание образа - простой вызов программы сжатия:
Making the SquashFS image
# cd /root/fscopy
# mksquashfs * ../filesystem.squash
Шаг 2а: Накладываем патч на ядро
Вам повезло, если ваше
ядро уже содержит поддержку SquashFS и UnionFS, который мы используем
позже. У тех, у кого установлена, к примеру, Ubuntu Feisty, уже есть
модули и не нужно больше ничего делать. Проверить свое счастье можно
простыми командами:
Проверка существующих модулей
# modprobe squashfs
# modprobe unionfs
# mount -o loop /root/filesystem.squash /mnt
Если все команды завершились успешно и вы увидели свою
squash-файловую систему в /mnt, тогда вам ничего с ядром не нужно
делать, просто пропускайте этот шаг. Если же вы мазохист, как и я,
придется компилировать свое ядро, содержащее SquashFS и UnionFS. К
сожалению, поддержка этих чрезвычайно полезных файловых систем не
включено в главную ветку ядра, поэтому придется накладывать патчи.
Загружаем и накладываем патчи
# cd /usr/src
# wget http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.22.9.tar.bz2
# tar xjf linux-2.6.22.9.tar.bz2
# wget http://switch.dl.sourceforge.net/sourceforge/squashfs/squashfs3.2-r2.tar.gz
# tar xzf squashfs3.2-r2.tar.gz
# cd linux-2.6.22.9
# patch -p1 < ../squashfs3.2-r2/kernel-patches/linux-2.6.20/squashfs3.2-patch
# wget -O- http://download.filesystems.org/unionfs/unionfs-2.1/unionfs-2.1.6_for_2.6.22.9.diff.gz | gunzip | patch -p1
Приведенные пакеты датированы октябрем 2007 и могут обновиться в
любой момент, так что по возможности, конечно, лучше скачать новые
версии. На момент написания статьи все команды завершались успешно.
После успешного наложения патчей вы можете собрать ядро как обычно, включая все драйвера и модули, но также включая и два патча:
Выбор патчей
File systems ->
Layered filesystems ->
<M> Union file system Miscellaneous filesystems ->
<M> SquashFS 3.2 - Squashed file system support
Шаг 2б: Загружаем ядро
Собрали ли вы свое ядро или
использовали включенное в дистрибутив, все равно следующий шаг -
заставить его загрузиться. Так как в USB Flash-носителе уже есть
файловая система, это сведется к копированию файла ядра и исполнению
одного волшебного трюка. Этот трюк носит имя SysLinux, небольшой пакет,
который может загрузить ядро во множестве ситуаций. Для использования
SysLinux сначала его нужно установить; на Gentoo Linux это делается
следующим образом:
Установка SysLinux
# emerge syslinux
SysLinux предоставляет загрузочную файловую систему, из которой
может быть запущено ядро Linux; это делается путем добавления
загрузочного сектора и системного файла на раздел FAT, на который его
нужно установить.
Такая установка проста:
Помещаем SysLinux на Flash-носитель
# syslinux /dev/sda1
При загрузке SysLinux будет произведен поиск конфигурационного
файла syslinux.cfg для определения последующих действий. Можно сделать
конфигурацию сложной, с несколькими ядрами и с различными вариантами
загрузки, чтобы потом выбирать их в процессе загрузки. В нашем же
случае требуется наипростейшая конфигурация:
syslinux.cfg: Загрузка ядра
default kernel.img
Такая конфигурация заставит SysLinux искать файл ядра Linux с
именем kernel.img и загрузить его. Теперь все, что остается сделать -
скопировать ядро на USB-носитель. В зависимости от пути, который вы
выбрали выше, ядро может быть либо в каталоге с исходниками ядра, либо
в каталоге /boot. Я сам компилировал свое ядро, поэтому я командовал
следующее:
Копируем образ ядра
# mount /dev/sda1 /mnt/flash
# cp /usr/src/linux-2.6.22.9/arch/i386/boot/bzImage /mnt/flash/kernel.img
Теперь мы сможем загрузить Linux с Flash-носителя. К сожалению,
пройти далеко не получится - вы наткнетесь на kernel panic; не было
указано корневое устройство, поэтому загрузка не состоится. Чтобы
дальнейшая часть операционной системы загрузилась, требуется временная
корневая файловая система.
Шаг 3а: Создание начальной корневой системы
Теперь нужно
использовать SquashFS-образ в качестве корневого диска. Однако здесь
есть некоторые проблемы: образ SquashFS доступен только для чтения, а
нам нужна система, которая справляется и с такими вещами, как логи. К
счастью, есть способ решения этой проблемы, он состоит в том, чтобы
наложить временную файловую систему на образ SquashFS, чтобы изменения
проводились лишь в ней.
Для этого нужна прослойка между ядром и образом файловой системы,
которая может производить такое наложение и затем возвращаться назад к
реальному образу корневой системы. Ядро позволяет сделать это с помощью
initrd (initial root disk - начальный корневой диск), который может
быть загружен в оперативную память. initrd может содержать все что
угодно, однако нужно учитывать, что initrd-диск полностью загружается в
память, и нужно оставить место для основного образа.
Создать начальный корневой диск проще, чем собрать
SquashFS-образ. Начнем с выделения места для образа. Я выбрал 8 Мб,
этого хватит для всех наших нужд.
Инициализация initrd
# dd if=/dev/zero of=/root/initrd bs=1M count=8
# mke2fs /root/initrd
# mount -o loop /root/initrd /mnt/initrd
После того, как пустой образ initrd появился в вашей файловой
системе и смонтирован, мы можем добавлять в него файлы. Во-первых,
нужна базовая структура каталогов и немного node для устройств:
Заполняем initrd
# cd /mnt/initrd
# mkdir -p bin dev etc lib/modules mnt proc sbin tmp usr/bin usr/sbin var/lib
# for a in {0,1,2,3,4,5,6,7}; do mkdir mnt/$a; done
# for a in {tty*,console,null}; do cp `find /dev -maxdepth 1 -name "$a" -type c` dev; done
# for a in {hd*,sd*,fd*}; do cp `find /dev -maxdepth 1 -name "$a" -type b` dev; done
Шаг 3б: Заполняем initrd
Итак, создана базовая структура
initrd. Теперь нужна поддержка команд наподобие sh, mount и cp, чтобы
система могла на начальном этапе что-то делать. К счастью есть отличный
пакет под названием BusyBox, который компилирует все утилиты, которые
могут понадобиться, в один бинарный файл. Я не буду останавливаться на
конфигурировании BusyBox, скажу лишь, что это очень похоже на
конфигурацию ядра Linux: предоставляется система меню, в которой
выбираются нужные пункты. Обратите внимание, что нужно компилировать
BusyBox в статически слинкованный бинарный файл, иначе для загрузки
initrd потребуется не только исполняемый файл, но и все библиотеки, от
которых он зависит.
Компилируем BusyBox
# wget http://busybox.net/downloads/busybox-1.7.2.tar.bz2
# tar xjf busybox-1.7.2.tar.bz2
# cd busybox-1.7.2
# make menuconfig
# make
Специально для тех, кто не желает проводить вышеприведенные
процедуры, я выложил копию initrd. Она содержит базовую структуру
каталогов, node устройств и статически-скомпилированную копию BusyBox:
http://oopsilon.com/software/linux-initrd.gz [1.1 Мб]
В этом архиве отсутствуют копии модулей ядра. Вы можете взять их
из своего каталога /lib/modules и просто скопировать их поверх initrd;
в следующем примере я копирую модули из заранее скомпилированного ядра
2.6.22.9:
Копируем модули ядра в initrd
# cp -a /lib/modules/linux-2.6.22.9 /mnt/initrd/lib/modules
Шаг 3в: Монтируем реальный корень
Последний шаг в initrd -
указать его назначение. В данный момент это просто набор бинарных
файлов и node устройств. У нас есть начальный корневой диск и ядро, но
нет связи между ними: ядро должно загрузить initrd. Это делается путем
передачи параметров ядру при его загрузке, вот нужная конфигурация
SysLinux:
syslinux.cfg: включаем initrd
default kernel.img initrd=initrd.gz root=/dev/ram0 ramdisk_size=8192 rw init=/linuxrc
Как можно видеть из этой конфигурации, ядру было указано
запускать файл linuxrc на initrd, как инициализационный скрипт. Вот
простой linuxrc для initrd-образа, опубликованного выше. Все что, он
делает - загружает оболочку.
linuxrc: простой инициализационный файл
#!/bin/msh
mount -t proc proc /proc
clear
exec /bin/msh
Наверняка вы удивились, увидеть столь короткий скрипт. Он
использует лишь команды, поставляемые с BusyBox. Для решения нашей же
задачи мы монтируем SquashFS корневую файловую систему, накладываем
RAM-диск повер и запускаем новую корневую систему, с помощью такого
скрипта:
linuxrc: переключаемся на реальную корневую ФС
#!/bin/msh
echo Initial root disk loaded. Proceeding.
# Монтируем файловую систему proc и Flash-накопитель
mount -t proc proc /proc
mount /dev/sda1 /mnt/0
# Находим SquashFS-образ на Flash-накопителе и монтируем его
mount /mnt/0/newroot.sfs /mnt/1
# Монтируем временную файловую систему для использовании в качестве наложения
mount -t tmpfs -o size=100M tmpfs /mnt/2
# Выполняем наложение с помощью UnionFS с tmpfs доступной как для чтения, так и для записи
# и со SquashFS доступной только для чтения
mount -t unionfs -o dirs=/mnt/2=rw:/mnt/1=ro /mnt/1 /mnt/3
# Переходим к новой корневой системе
cd /mnt/3
mkdir initrd
pivot_root . initrd
# Заходим в новую корневую ФС и запускаем процесс init
exec chroot . /sbin/init </dev/console >/dev/console 2>&1
В зависимости от вашего ядра, возможно, придется изменить имя,
по которому идет обращение к Flash-накопителю. Если вы загружаете
initrd с помощью простого скрипта оболочки, который был приведен выше,
посмотрите вывод команды dmesg, вы увидите, откуда было загружено ядро.
Шаг 4: Финальная часть и проверка
С написанием linuxrc образ initrd завершен и может быть отмонтирован и сжат:
Сжатие initrd
# umount /mnt/initrd
# gzip -9 /root/initrd
# cp /root/initrd.gz /mnt/flash
# umount /mnt/flash
Здесь можно считать, что наша задача решена. Вставьте Flash-накопитель
в компьютер и наблюдайте за процессом загрузки: он должен выглядеть
примерно как и загрузка с жесткого диска. Если загрузка не получается,
вот некоторые возможные причины:
-
Kernel panic: нет root или нет init. Запомните, чтобы
использовать initrd-образы, их поддержка должна быть встроена в ядро (а
не просто скомпилирована в виде модуля); к тому же параметры должны
быть указаны в конфигурационном файле SysLinux.
-
initrd выводит сообщение, но больше ничего не происходит.
Убедитесь, что указано правильное имя Flash-накопителя в скрипте
linuxrc; если скрипт не может найти Flash-накопитель, загрузка не
продолжится.
-
init не удается прочитать /dev/initctl. Скрипт SYSV init в
большинстве Linux-дистрибутивов использует FIFO-пайп /dev/initctl для
взаимодействия и смены уровней выполнения; если этот node не существует
в корневой файловой системе SquashFS, init рухнет.
Если вас преследуют другие неясные ошибки, не стесняйтесь
писать мне или своим местным знатокам Linux; также дайте мне знать,
если это руководство вам реально помогло.
|