LFS: Темная сторона силы. Часть 3
Итак, пришло время расставить последние точки над «i» и рассказать о том, как из кучи исполняемых файлов и библиотек, которые мы героически собирали и настраивали в прошлой статье получить, наконец, Linux. Эта подсистема инициализации долгое время использовалась в Linux и была стандартом «де-факто». Однако время идёт, и нельзя в общем-то называть данный подход устаревшим. Гораздо точнее заметить что эта подсистема инициализации, в тренде развития систем семейства GNU/Linux, уступила свое место systemd, рожденной в недрах корпорации Red Hat. Существуют дистрибутивы до сих пор использующие скрипты инициализации. Однако все популярные линуксы практически поголовно пришли к использованию systemd, причем последним сдался консервативный Debian со своей дочкой Ubuntu.Вообще-то я жалею, что сразу не стал собирать вариант LFS, использующий systemd. Просто, после первой неудачной попытки сборки не хотелось отклонятся от стабильной траектории. Возможно я ещё вернусь к этому вопросу, возможно так же что и не вернусь. Время покажет. А пока рассмотрим основные принципы работы скриптов инициализации System V.При загрузке системы, загрузчик читает ядро Linux с корневого раздела загрузочного носителя. После инициализации ядра управление передается процессу init, получающему идентификатор PID=1 (физически расположен в файле /sbin/init). Данный процесс работает в соответствии с настройками, указанными в файле /etc/inittab.
По умолчанию используется семь уровней инициализации системы
0 — остановка системы 1 — загрузка системы в однопользовательском режиме 2 — загрузка системы в многопользовательском режиме без поддержки сети 3 — загрузка системы в многопользовательском режиме с поддержкой сети 4 — не используется 5 — загрузка системы в многопользовательском режиме с поддержкой сети и графическим входом в систему 6 — перезагрузка системы Каждому уровню запуска соответствуют скрипты инициализации, в случае LFS расположенные в каталоге /etc/rc.d/init.d/. Их не придется писать самостоятельно — авторы LFS позаботились об этом, включив всё необходимое для настройки в пакет LFS-Bootscripts, который лежит по пути $LFS/source. Нам остается лишь установить данный пакет и произвести некоторые минимальный настройки
Предположим сборка пакетов на предыдущем этапе вас утомила, да и появились другие дела и вы выключили машину. Теперь вас снова необходимо попасть в собираемую систему. Для этого необходимо выполнить chrootМонтируем раздел с системой и VFS:
$ su — root # export LFS=/mnt/lfs # mount /dev/sda6 $LFS # mount -v --bind /dev $LFS/dev # mount -vt devpts devpts $LFS/dev/pts -o gid=5, mode=620 # mount -vt proc proc $LFS/proc # mount -vt sysfs sysfs $LFS/sys # mount -vt tmpfs tmpfs $LFS/run Выполняем смену корня: # chroot »$LFS» /usr/bin/env -i \ > HOME=/root TERM=»$TERM» PS1='[\u:\w]\$ ' \ > PATH=/bin:/usr/bin:/sbin:/usr/sbin \ > /bin/bash --login Перейдем в каталог с исходниками и установим нужный пакет # cd /source # tar -pxf lfs-bootscripts-20150222.tar.bz2 # cd lfs-bootscripts-20150222 # make install Ничего особенно хитрого и не предполагалось — скрипты просто установятся по нужным путям.А вот теперь приступим к настройке. Сгенерируем правила Udev:
# bash /lib/udev/init-net-rules.sh Дальше мануал предлагает нам проверить и отредактировать правило именования сетевых интерфейсов, расположенные в /etc/udev/rules.d/70-persistent-net.rules. И вот тут меня ждал облом — скрипт генерации выдал ошибку — не могу сгенерировать этот файл.Ответ есть в том же руководстве:
In some cases such as when MAC addresess have been assigned to a network card manually or in a virtual environment such as Qemu or Xen, the network rules file may not have been generated because addresses are not consistently assigned. In these cases, this method cannot be used.
Да, а вот случай с «Qemu or Xen» он как раз мой — я собирал под виртуальной машиной (VirtualBox, как известно использующий код Qemu). Ну что же, напишем правило именования сетевой карточки вручную. Причем теперь нам не надо использовать команду cat для создания файла — ведь мы собрали и настроили целый Vim! А Vim — это сила
# vim /etc/udev/rules.d/70-persistent-net.rules
Набиваем текст правила:
# net device e1000
SUBSYSTEM==«net», ACTION==«add», DRIVERS==»?*», ATTR{address}==»08:00:27: f8:4c:26», ATTR{dev_id}=»0×0», ATTR{type}=»1», KERNEL==«eth*», NAME==«eth0»
Данное правило, при обнаружении ядром сетевого устройства, с MAC-адресом, указанным нами в качестве атрибута, присваивает ему имя eth0. MAC-адрес вашей сетевой карты можно узнать дав в терминале команду:
# ip link
1: lo:
# tar -pxf dhcpcd-6.7.1.tar.bz2 # cd dhcpcd-6.7.1 # ./configure --libexecdir=/lib/dhcpcd --dbdir=/var/tmp # make # make install # cd … # rm -rf dhcpcd-6.7.1/ Кроме того, установим скрипты, необходимые dhcpcd для работы: # tar -pxf blfs-bootscripts-20150304.tar.bz2 # cd blfs-bootscripts-20150304 # make install-service-dhcpcd Ну и, наконец, создадим конфиг: # vim /etc/sysconfig/ifconfig.eth0 C содержимым:
ONBOOT=«yes» IFACE=«eth0» SERVICE=«dhcpcd» DHCP_START=» DHCP_STOP=»-k» Смысл параметров таков: ONBOOT=«yes» — запуск демона при загрузке системы IFACE=«eth0» — получение ip от сервера на интерфейсе eth0 SERVICE=«dhcpcd» — запускаемый сервис DHCP_START=» — параметры запуска DHCP_STOP=»-k» — параметры остановки (kill) Создадим файл /etc/hostname, в который пропишем имя хоста:
echo «lfs» > /etc/hostname После этих операций можно расчитывать на то, что после перезагрузки у нас поднимется сеть.Теперь займемся настройкой init.
# vim /etc/inittab Мануал предлагает следующий скрипт: # Begin /etc/inittab
# Уровень запуска системы по умолчанию — 3 id:3: initdefault:
# Инициализация системы (запускается при загрузке системы) si: sysinit:/etc/rc.d/init.d/rc S
# Запуск скрипта инициализации # для каждого уровня запуска
l0:0: wait:/etc/rc.d/init.d/rc 0 l1: S1: wait:/etc/rc.d/init.d/rc 1 l2:2: wait:/etc/rc.d/init.d/rc 2 l3:3: wait:/etc/rc.d/init.d/rc 3 l4:4: wait:/etc/rc.d/init.d/rc 4 l5:5: wait:/etc/rc.d/init.d/rc 5 l6:6: wait:/etc/rc.d/init.d/rc 6
# Реакция на нажатие Ctrl + Alt + Del — перезагрузка ca:12345: ctrlaltdel:/sbin/shutdown -t1 -a -r now
# Скрипт запускаемый в однопользовательском режиме su: S016: once:/sbin/sulogin
# Инициализация виртуальных терминалов 1:2345: respawn:/sbin/agetty --noclear tty1 9600 2:2345: respawn:/sbin/agetty tty2 9600 3:2345: respawn:/sbin/agetty tty3 9600 4:2345: respawn:/sbin/agetty tty4 9600 5:2345: respawn:/sbin/agetty tty5 9600 6:2345: respawn:/sbin/agetty tty6 9600
# End /etc/inittab
Строки этого файла состоят из четырех полей, разделенных двоеточиями:
Процесс /sbin/shutdown запускается при останове/перезагрузке системы
/sbin/agetty — создает и инициализирует виртуальный терминал.
Изменить уровень выполнения можно в процессе работы системы, дав команду от рута
# init
# runlevel или: # who -r Для верной работы системных часов создадим конфиг/etc/sysconfig/clock
# Begin /etc/sysconfig/clock
UTC=1
# Set this to any options you might need to give to hwclock, # such as machine hardware clock type for Alphas. CLOCKPARAMS=
# End /etc/sysconfig/clock Здесь указывается что аппаратные часы будут идти по универсальному всемирному времени, а поправку на временую зону мы уже внесли, когда настраивали пакет GLibc.
Теперь необходимо верно настроить консольный шрифт. Все необходимые нам локали мы установили вместе с GLibc. Нас интересует кирилица, поэтому создаем конфигурационный файл консоли в таком виде
/etc/sysconfig/console
# Begin /etc/sysconfig/console
UNICODE=»1» KEYMAP=«us» FONT=«UniCyr_8×16»
# End /etc/sysconfig/console UNICODE=»1» — указываем, что будем использовать локаль в кодировке UTF-8 KEYMAP=«us» — раскладка консоли FONT=«UniCyr_8×16 — задаем кирилический консольный шрифт Зададим локаль, настроив профиль bash по-умолчанию
/etc/profile
# Begin /etc/profile
export LANG=ru_RU.UTF-8
# End /etc/profile Для порядка следует так же «забиндить» некоторые клавиатурные настройки, используемые оболочкой и библиотекой readline, отвественной за клавиатурный ввод
/etc/inputrc
# Begin /etc/inputrc
# Не выводим ничего в первой строке set horizontal-scroll-mode Off
# Разрешает 8-и битный ввод set meta-flag On set input-meta On
# Выключаем конвертацию 8-ого бита set convert-meta Off
# Оставляем 8-ой бит для экрана set output-meta On
# ничего, видимый или слышимый set bell-style none
# Все следующее — карта соответствий escape-последовательностей значений, # содержащихся внутри первого аргумента, к специфическим функциям # readline
»\eOd»: backward-word »\eOc»: forward-word
# for linux console »\e[1~»: beginning-of-line »\e[4~»: end-of-line »\e[5~»: beginning-of-history »\e[6~»: end-of-history »\e[3~»: delete-char »\e[2~»: quoted-insert
# for xterm »\eOH»: beginning-of-line »\eOF»: end-of-line
# for Konsole »\e[H»: beginning-of-line »\e[F»: end-of-line
# End /etc/inputrc Отдельное внимание уделять именно этому конфигу не хочется, поэтому детальный его разбор оставим как домашнее задание.
Следующий конфиг определяет имена оболочек, использование которых разрешено в системе
/etc/shells
# Begin /etc/shells
/bin/sh /bin/bash
# End /etc/shells На этом первичная настройка системы окончена, осталось всего несколько последних штрихов
Ирония заключается в том, что сборка ядра — это меньшая часть всей работы. Идём в каталог /sources и распаковываем (в очередной раз) исходники: # cd /sources # tar -pxf linux-3.19.tar.bz2 # cd linux-3.19 Подготавливаем ядро к компиляции, проверяя дерево исходников: # make mrproper Необходимо сгенерировать конфиг сборки ядра: # make menuconfig Запустится конфигуратор, знакомый многим не по наслышке.Что можно сказать о требуемой конфигурации. Во-первых мануал по сборке LFS рекомендует проверить установку следующей опции:
Поддержка монтирования виртуальной файловой системы devtmpfs в /dev — динамическое создание файлов устройств в памяти и монтирование /dev на ранней стадии загрузки ядра.
Всё остальное зависит от вашей аппаратной конфигурации и тех функций, которые вы бы хотели включить в ядро. Я, например, добавил лишь полную поддержку NTFS, остальное оставил по-умолчанию.
Сохраняем конфиг как .config, выходим из конфигуратора и собираем ядро
# make Устанавливаем модули ядра: # make modules_install Копируем собранное ядро в каталог /boot, переименовывая его: # cp -v arch/x86_64/boot/bzImage /boot/vmlinuz-3.19-lfs-7.7 Сохраняем map-файл и конфиг собранного ядра: # cp -v System.map /boot/System.map-3.19 # cp -v .config /boot/config-3.19 Доустанавливаем документацию: # install -d /usr/share/doc/linux-3.19 # cp -r Documentation/* /usr/share/doc/linux-3.19 Создаем конфиги для опциональной загрузки модулей, например для поддержки USB: # install -v -m755 -d /etc/modprobe.d # vim /etc/modprobe.d/usb.conf /etc/modprobe.d/usb.conf # Begin /etc/modprobe.d/usb.conf
install ohci_hcd /sbin/modprobe ehci_hcd; /sbin/modprobe -i ohci_hcd; true install uhci_hcd /sbin/modprobe ehci_hcd; /sbin/modprobe -i uhci_hcd; true
# End /etc/modprobe.d/usb.conf Прибираемся за собой: # cd … # rm -rf linux-3.19/
Создаем конфиг монтирования /etc/fstab. У меня он выглядел вот так:/etc/fstab
/dev/sda6 / ext4 defaults 1 1 /dev/sda2 swap swap pri=1 0 0 proc /proc proc nosuid, noexec, nodev 0 0 sysfs /sys sysfs nosuid, noexec, nodev 0 0 devpts /dev/pts devpts gid=5, mode=620 0 0 tmpfs /run tmpfs defaults 0 0 devtmpfs /dev devtmpfs mode=0755, nosuid 0 0 /dev/sda2 и /dev/sda6 — соответственно своп и корневой раздел LFS-системы.
Так как я собирал LFS из системы уже установленной на HDD (Arch Linux), то для первой её загрузки я предпочел воспользоваться загрузчиком Grub2 уже установленном на жесткий диск. Для настройки я использовал конфиг для создания кастомного пункта загрузочного меню.
Выходим из собранной системы:
# loguot И в хост-системе редактируем файл:/etc/grub.d/40_custom
#!/bin/sh exec tail -n +3 $0 # This file provides an easy way to add custom menu entries. Simply type the # menu entries you want to add after this comment. Be careful not to change # the 'exec tail' line above.
menuentry «GNU/Linux, Linux 3.19-lfs-7.7» { insmod=ext2 set root=(hd0,6) linux /boot/vmlinuz-3.19-lfs-7.7 root=/dev/sda6 ro } После чего перегенерируем конфиг загрузчика: # grub-mkconfig -o /boot/grub/grub.cfg И… перезагружаемся: # systemctl reboot Если всё в порядке и мы не ошиблись, то появится меню выбора ОС:
Выбираем последний пункт нашей LFS-системы…
Да! Началась загрузка, видно как поднимается сетевой интерфейс, ждем когда мы получим ip и… экран логина:
Логинимся как root:
И запускаем что-нибудь, что покажет нам правильную настройку локали:
# vim Русский язык там где и должен быть. Проверяем сеть:
# ping ya.ru И наблюдаем, как идет пинг:
Ну что же,
Мы попробовали свои силы в сборке линукса «с нуля» (from scratch). И нам это удалось. У нас есть минимальная работающая система, с которой дальше можно делать всё что угодно — перенести на реальное железо, попробовать превратить в домашнюю систему. Можно полюбоваться и забыть, ибо поддерживать домашнюю систему без пакетного менеджера довольно тяжко.Я делал это just for fun, получил некоторые новые знания, и в целом остался доволен, чего желаю и моим читателям.
P.S.: собранную мной систему можно скачать тут. По ссылке — образ диска для VirtualBox. Параметры входа таковы
login: rootpasswd: 123456
login: maisvendoopasswd: maisvendoo