Можайский Сергей (frenzytechnix) wrote,
Можайский Сергей
frenzytechnix

Categories:

Frenzy в деталях: процесс загрузки

Так уж получилось, что документирование Frenzy всегда было на последнем месте в списке приоритетов моих задач по разработке. И хотя документация по Frenzy все-таки есть, в основном она описывает только использование системы, документации по внутренностям системы для разработчиков практически нет. Поэтому, чтобы и самому не забыть о том как там все устроено, и чтобы другим было понятнее что и как сделано в Frenzy, я и написал эту статью.

Сегодня я расскажу о том, как в Frenzy устроен процесс загрузки. В общих чертах он описан в документации, я же расскажу о тех деталях, которые в документацию не вошли.



Если не вдаваться в подробности, FreeBSD загружается так: сначала с помощью freebsd loader загружается ядро, потом монтируется корневая файловая система, с нее запускается /sbin/init, который в свою очередь запускает /etc/rc, из него выполняется куча скриптов, и в конце концов мы получаем приглашение login. Для нормальной загрузки с LiveCD нам нужно сымитировать этот процесс. Что ж, приступим.

Для загрузки ядра FreeBSD с CD есть специальный файл /boot/cdboot, который мы при сборке ISO-образа записываем в загрузочную область, тут все достаточно просто.

Теперь надо определиться, откуда будем брать корневую файловую систему. Проще всего, конечно, использовать файловую систему самого компакт-диска, с которого мы загружаемся. Но поскольку сменить корневую файловую систему на лету нельзя, загрузить такой LiveCD полностью в оперативную память или из образа с HDD не выйдет. Поэтому в Frenzy я использовал тот же метод, что и в установочном диске обычной FreeBSD - там в качестве корневой файловой системы используется небольшой сжатый gzip-ом файл-образ, который загружается сразу в память.

Добавляем в /boot/loader.conf строчки:
mfsroot_load="YES"
mfsroot_type="mfs_root"
mfsroot_name="/boot/frenzyroot"

Сама файловая система находится в /boot/frenzyroot.gz. Для экономии памяти туда помещен минимум программ. Кроме init и sh, без которых никак не обойтись, мы добавим туда нужные нам утилиты - cp, echo, test, mdconfig, mount, mount_msdosfs, mount_ntfs, mount_cd9660, mount_nullfs, mount_unionfs, umount и sleep. А для еще большей экономии мы сгенерируем с помощью crunchgen один статический бинарный файл, который будет содержать все эти утилиты, потом на этот файл создадим симлинки с нужными именами.

Итак, у нас есть минимальная корневая файловая система, после ее подключения ядро запустит /sbin/init, и он в свою очередь должен запустить /etc/rc. Однако тут мы делаем первый хак - /etc/rc в нашей корневой файловой системе мы заменили на собственный скрипт. Потом, конечно, мы запустим настоящий /etc/rc, однако предварительно мы должны все подготовить.

Сначала мы ищем устройство, на котором находится сжатый образ frenzy.uzip. Порядок поиска - флешки, CD, UFS и FAT-разделы жестких дисков. Флешки стоят в порядке поиска перед CD для того, чтобы можно было устроить комбинированную загрузку - ядро с CD, а все остальное с флешки (и CD вытащить можно). Пробуем подключать по очереди, как только находим на подключенном устройстве файл образа - продолжаем дальше. Файловая система загрузочного устройства будет смонтирована в /Frenzy/boot.

Небольшое отступление - что такое mount_nullfs и mount_unionfs
В процессе загрузки Frenzy активно используются этим методы монтирования. mount_nullfs позволяет смонтировать один каталог поверх другого, при этом содержимое исходного каталога доступно не будет. В отличие от него, mount_unionfs позволяет смонтировать каталоги друг поверх друга, объединяя содержимое обоих каталогов. В случае совпадения имен файлов приоритетным является тот, который находится в верхнем "слое". Если с файлами в каталоге производятся какие-то действия, то все изменения сохраняются также в верхнем "слое".


Теперь подключаем сжатую файловую систему из образа, монтируется она в каталог /Frenzy/fs. С помощью mount_nullfs мы подключаем каталоги /bin, /sbin, /usr, /var, /etc, /root из сжатой системы в корневую, выглядит это примерно так:

/Frenzy/fs ---- bin ------------ etc ----------- root --- sbin --- usr --- var --
                 |                |               |        |        |       |
                 V                V               V        V        V       V
корневая FS -- /bin -- /boot -- /etc -- /mnt -- /root -- /sbin -- /usr -- /var --
(на этой и следующих схемах прямыми линиями изображены подключения mount_nullfs, плюсиками - mount_unionfs)

Теперь нам нужно сделать так, чтобы система не подозревала что все у нас только для чтения. Для этого сымитируем возможность записи с помощью mount_unionfs, создав отдельный диск в оперативной памяти и смонтировав каталоги с него поверх имеющихся. Увы, смонтировать RAM-диск сразу поверх всей корневой файловой системы не получится, mount_unionfs сделать это не даст, т.к. получится рекурсия. RAM-диск мы делаем с помощью mdconfig и монтируем его в /Frenzy/mfs. После всех этих операций получаем вот такой "бутерброд":

/Frenzy/mfs -------------------- etc --- mnt --- root ------------ usr --- var --
                                  +       |       +                 +       +
                                  V       |       V                 V       V
/Frenzy/fs ---- bin ------------ etc -----|----- root --- sbin --- usr --- var --
                 |                |       |       |        |        |       |
                 V                V       V       V        V        V       V
корневая FS -- /bin -- /boot -- /etc -- /mnt -- /root -- /sbin -- /usr -- /var --


Каталог mnt мы монтируем через mount_nullfs, т.к. в /mnt у нас изначально нет файлов, и делать mount_unionfs было бы излишне. Записываемый /mnt нам нужен для того, чтобы потом создавать каталоги точек монтирования. А вот каталог /usr на этом этапе мы пока записываемым не делаем, потому что впереди нас ждет подключение модулей FEM.

Тепер немного отвлечемся и рассмотрим, как реализована загрузка в память и загрузка из образа на жестком диске. Процессы эти в общем-то идентичны - нам нужно сменить файловую систему в /Frenzy/fs, подключив ее из другого frenzy.uzip - скопированного в память либо уже лежащего на одном из разделов жесткого диска. Поскольку у нас пока нет процессов, которые блокируют файловую систему, препятствуя ее размонтированию, дело обстоит достаточно просто - мы размонтируем верхние два "слоя" нашего бутерброда, после чего монтируем их снова, но уже взяв другой frenzy.uzip в качестве базы.

И только теперь мы готовы подключать модули. Модуль представляет собой сжатый образ каталога /usr, содержащий файлы из одного или нескольких пакаджей, который в дальнейшем будет подключен поверх уже имеющейся /usr. Но поскольку в пакаджах содержатся еще и install-скрипты (например, некоторые пакеты после установки добавляют в систему нового пользователя), простого монтирования будет недостаточно. Поэтому после подключения всех модулей будет вызвана процедура fem_postinstall, отвечающая за запуск install-скриптов из пакаджей.

Файловые системы модулей монтируются в каталоги, создаваемые в /Frenzy/ramdisk/fem. После этого они по очереди с помощью mount_unionfs друг поверх друга монтируются в /usr. И наконец, после того как все модули будут подключены, мы делаем /usr записываемым, подключая поверх него каталог usr из RAM-диска. В итоге выходит вот такая структура файловой системы:

/Frenzy/mfs -------------------- etc --- mnt --- root ------------ usr --- var --
                                  +       |       +                 +       +
                                  +       |       +                 +       +
                                  +       |       +               модуль2   +
                                  +       |       +                 +       +
                                  +       |       +               модуль1   +
                                  +       |       +                 +       +
                                  V       |       V                 V       V
/Frenzy/fs ---- bin ------------ etc -----|----- root --- sbin --- usr --- var --
                 |                |       |       |        |        |       |
                 V                V       V       V        V        V       V
корневая FS -- /bin -- /boot -- /etc -- /mnt -- /root -- /sbin -- /usr -- /var --


Предполагаю, что к этому моменту у вас в голове вертится мысль "Нафига так сложно???". Ответ таков: в FreeBSD, увы, нет функции под названием pivot_root, которая существует в Linux. А функция эта позволяет переключить корневую файловую систему на уровне ядра (не как chroot, который меняет корневую FS для запущенного в нем процесса). Тогда весь этот процесс выглядел бы на порядок проще.

Ну вот и все, файловую систему мы подготовили, можно запускать системный /etc/rc. Вернее, можно было бы. Дело в том, что rc-скрипты среди всего прочего выполняют монтирование файловых систем из /etc/fstab и запуск на них fsck. Нам это совсем не нужно, поскольку сломает всё, что мы так долго строили. Так что приходится делать еще один хак и ряд скриптов, отвечающих за эти процессы, слегка модифицировать, добавив в их начало код "если мы загружаемся с CD, то выполнение команд пропустить".

Вот собственно и все. Посмотреть на скрипты, которые отвечают за процесс загрузки, можно в SVN-репозитории.

Продолжение, возможно, последует (хотелось бы узнать из комментариев, что еще вас интересует)
Tags: freebsd, frenzy
Subscribe

  • Frenzy 1.4

    Тихо, незаметно и совершенно неожиданно для меня вышла Frenzy 1.4. Спасибо Егору за то, что нашел время и собрал-таки исошники. Конечно, многие…

  • Я просто оставлю это здесь

    Из обсуждения Frenzy 1.2-reincarnation на опеннете. Michael Shigorin: >> Что меня поразило ещё тогда, когда technix@ рассказывал про Frenzy…

  • Frenzy 1.2 (LifeForce) - "ненастоящий" релиз

    Не так давно мне снова потребовался FreeBSD'шный LiveCD. Однако после запуска Frenzy 1.1 на имеющемся железе я с сожалением констатировал, что она…

  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 8 comments