Pull to refresh

LFS: Темная сторона силы. Часть 3

Reading time10 min
Views21K

Предисловие


Итак, пришло время расставить последние точки над «i» и рассказать о том, как из кучи исполняемых файлов и библиотек, которые мы героически собирали и настраивали в прошлой статье получить, наконец, Linux.

1. Подсистема инициализации Unix System V


Эта подсистема инициализации долгое время использовалась в 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. Нам остается лишь установить данный пакет и произвести некоторые минимальный настройки

2. Установка LFS-Bootscripts, настройка сети, /etc/inittab


Предположим сборка пакетов на предыдущем этапе вас утомила, да и появились другие дела и вы выключили машину. Теперь вас снова необходимо попасть в собираемую систему. Для этого необходимо выполнить 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}="0x0", ATTR{type}="1", KERNEL=="eth*", NAME=="eth0"

Данное правило, при обнаружении ядром сетевого устройства, с MAC-адресом, указанным нами в качестве атрибута, присваивает ему имя eth0. MAC-адрес вашей сетевой карты можно узнать дав в терминале команду:

# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:f8:4c:26 brd ff:ff:ff:ff:ff:ff

Параметр link/ether у устройства enp03s — это и есть искомый мак. Устройство именовано хост-системой (а мы помним, что мы лишь сменили корень и значение некоторых переменных окружения, запустив «боевой» bash, но мы остались в хост-системе и все устройства нам доступны).

Теперь можно наcтроить доступ в сеть. В моем случае, при сборке на ВМ, она выходит в сеть через виртуальный NAT, поэтому требуется наличие клиента DHCP. Мы специально скачали пакет dhcpcd, теперь мы соберем его и установим в системе:

# 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

Строки этого файла состоят из четырех полей, разделенных двоеточиями:

<id>:<runlevels>:<action>:<process>


  • id — идентификатор строки — произвольная комбинация, содержащая от 1 до 4 символов. Допускается использование двух строк с одинаковыми идентификаторами.
  • runlevels — уровни выполнения, на которых задействуется данная строка. Уровни перечисляются цифрами безо всяких разделителей, например 235 (работать на 2, 3 и 5 уровнях)
  • process — процесс, который должен запускаться на указанных уровнях (имя программы, вызываемой на указанных уровнях выполнения)
  • action — действие, определяющее дополнительные условия выполнения команды. Возможные значения

    • respawn — перезапуск процесса при его завершении
    • once — выполнить процесс только один раз при переходе на указанный уровень
    • wait — запуск процесса и переход init в режим ожидания его запуска
    • sysinit — действия, выполняемы init в самом начале, ещё до перехода на какой-либо уровень выполнения. Процессы помеченные таким образом запускаются до процессов помеченных как boot и bootwait
    • boot — процесс будет запущен на этапе загрузки системы независимо от уровня выполнения
    • bootwait — то же самое что и boot, но с переводом init в режим ожидания выполнения.
    • powerwait — действия, выполняемые при обрыве питания. Предполагает наличие UPS
    • ctrlaltdel — дейсвие выполняется по нажатию «трех волшебных кнопок»
    • off — игнорировать данный элемент


Для реализации действий на различных уровнях выполнения в LFS предусматривается скрипт /etc/rc.d/init.d/rc, принимающий в качестве параметра уровень запуска.

Для выполнения однопользовательского входа в случае сбоя загрузки вызывается команда /sbin/sulogin, этот однопользовательский вход и выполняющая.

Процесс /sbin/shutdown запускается при останове/перезагрузке системы

/sbin/agetty — создает и инициализирует виртуальный терминал.

Изменить уровень выполнения можно в процессе работы системы, дав команду от рута

# init <runlevel>

указав в качестве параметра уровень запуска. Известная большинству команда halt это алиас на init 0, а команда reboot — алиас на init 6.

Узнать текущий уровень запуска можно дав команду:

# runlevel

или:

# who -r

3. Часы, локализация и прочие приятные мелочи


Для верной работы системных часов создадим конфиг

/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_8x16"

# End /etc/sysconfig/console


  • UNICODE=«1» — указываем, что будем использовать локаль в кодировке UTF-8
  • KEYMAP=«us» — раскладка консоли
  • FONT=«UniCyr_8x16 — задаем кирилический консольный шрифт


Зададим локаль, настроив профиль 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


На этом первичная настройка системы окончена, осталось всего несколько последних штрихов

4. Сборка ядра


Ирония заключается в том, что сборка ядра — это меньшая часть всей работы. Идём в каталог /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/


5. Монтирование файловых систем и настройка загрузчика


Создаем конфиг монтирования /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: root
passwd: 123456

login: maisvendoo
passwd: maisvendoo
Tags:
Hubs:
Total votes 20: ↑20 and ↓0+20
Comments11

Articles