Созданиe initramfs
Initramfs, сокращенно от “initial RAM file system”, является приемником initrd (initial ramdisk). Это cpio (copy in and out) архив исходной файловой системы, который загружается в память во время процесса запуска Linux. Linux копирует содержимое архива в rootfs (которая может быть основана на ramfs либо на tmpfs), а затем запускает init. Init предназначен для выполнения определенных задач до того, как реальная или финальная файловая система будет установлена поверх rootfs. Таким образом, initramfs должен содержать все драйвера устройств и инструменты, необходимые для установки конечной корневой файловой системы.
- Скачиваем busybox (вы можете скачать более новую версию):
wget https://busybox.net/downloads/busybox-1.26.2.tar.bz2
tar -xvf busybox-1.26.2.tar.bz2
2. Собираем busybox из исходников:
cd busybox-1.26.2
make defconfig
make menuconfig
В меню Busybox Settings выбираем Build Options, и ставим галочку напротив Build BusyBox as a static binary (no shared libs). Далее указываем выходную папку для бинарников и собираем busybox.
make
make CONFIG_PREFIX=./../busybox_rootfs install
3. Создаем иерархию каталогов для initramfs:
mkdir -p initramfs/
cd initramfs/dev
sudo mknod sda b 8 0
sudo mknod console c 5 1
Так же копируем все из папки busybox_rootfs в папку initramfs. Дальше создаем в корне initramfs файл init, и пишем в него следующее:
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
exec /bin/sh
И даем ему права на исполнение:
chmod +x init
4. Создаем сам initramfs:
find . -print0 | cpio --null -ov --format=newc > initramfs.cpio
gzip ./initramfs.cpio
5. Скачиваем и собираем ядро (вы можете скачать более новую версию):
wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.11.6.tar.xz
tar -xvf linux-4.11.6.tar.xz
make x86_64_defconfig
make kvmconfig
make -j2
Образ ядра будет лежать в /arch/x86_64/boot/bzImage.
6. Дальше копируем куда-то наш initramfs и ядро, заходим в эту дерикторию и запускаем qemu:
qemu-system-x86_64 -kernel ./bzImage -initrd ./initramfs.cpio.gz -nographic -append "console=ttyS0"
7. Если все было выполнено правильно, то загрузится ядро и запустится shell.
Ubuntu/Mint/Kali загружается в initramfs BusyBox (РЕШЕНО)
В этой статье мы покажем, как решить проблемы, которые возникают, когда компьютер под управлением Ubuntu, Mint Linux или Kali Linux не загружается и во время инициализации initramfs появляется только приглашение busybox. В этой ситуации возможно получить доступ и использовать только командную строку initramfs.
initramfs — это исходная файловая система на основе tmpfs в ОЗУ, которая не использует отдельное блочное устройство. Как и initrd, она содержит инструменты и сценарии для монтирования файловых систем до вызова init, расположенного в корневой файловой системе.
Восстановление неработающего суперблока Ext4 в Linux
Если Ubuntu вылетает в busybox во время инициализации initramfs, возможно, на диске повреждён суперблок.
Несколько копий суперблока хранятся в Linux. Чтобы восстановить систему в случае возникновения этой проблемы, вам необходимо загрузиться с аварийного образа/диска Live CD и запустить терминал. После загрузки введите в терминал следующую команду:
sudo fdisk -l|grep Linux|grep -Ev 'swap'
Команда возвращает информацию о вашем томе:
/dev/sdb2 4096 83884031 83879936 40G Linux filesystem
Запомните имя тома и укажите его в следующей команде:
sudo dumpe2fs /dev/sdb2 | grep superblock
Команда покажет список резервных суперблоков:
Мы воспользуемся вторым резервным суперблоком для замены повреждённого (можно использовать любой суперблок, кроме первичного). Проверьте диск с помощью резервного суперблока:
sudo fsck -b 98304 /dev/sdb2 -y
Если вы получите такой результат:
fsck from util-linux 2.31.1 e2fsck 1.44.1 (24-Mar-2018) /dev/sdb2 is mounted. e2fsck: Cannot continue, aborting
umount /dev/sdb2
После успешной замены суперблока вы получите такое сообщение:
fsck from util-linux 2.31.1 e2fsck 1.44.1 (24-Mar-2018) /dev/sdb2 was not cleanly unmounted, check forced. Pass 1: Checking inodes, blocks, and sizes Pass 2: Checking directory structure Pass 3: Checking directory connectivity Pass 4: Checking reference counts Pass 5: Checking group summary information Free blocks count wrong for group #231 (32254, counted=32253). Fix? yes Free blocks count wrong for group #352 (32254, counted=32248). Fix? yes Free blocks count wrong for group #358 (32254, counted=27774). Fix? yes . /dev/sdb2: ***** FILE SYSTEM WAS MODIFIED ***** /dev/sdb2: 85986/905464576 files (0.2% non-contiguous), 3904682/905464576 blocks
Затем отключите загрузочный носитель и перезагрузите компьютер. Всё должно работать исправно.
На моей практике показанный выше метод срабатывает практически всегда. Но однажды я столкнулся с исключением — система продолжала загружаться в initramfs даже после восстановления суперблока. Я сделал несколько попыток используя различные резервные копии суперблока, но это также ничего не изменило. В результате мне помогло следующее: в меню загрузки GRUB я выбрал пункт «Дополнительные опции» и выбрал другое ядро. Окончательно решить проблему удалось удалив ядро XanMod с переходом на стандартную версию ядра для данного дистрибутива. После удаления ненужных ядер необходимо выполнить обновление настроек GRUB с помощью команды:
sudo update-grub
Fsck Boot Error: Unexpected Inconsistency
Второй вариант проблемы initramfs (BusyBox) включает следующее сообщение в окне терминала:
/dev/sda1: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY The root filesystem on /dev/sda1 requires a manual fsck.
Если вы его не видите, попробуйте ввести в (initramfs)
exit
в окне терминала. Ошибка может появиться после того, как вы это сделаете.
В сообщении будет показан том, на котором требуется выполнить ручную проверку диска. Выполните следующую команду в приглашении initramfs:
fsck /dev/sda1 -y
После завершения проверки диска перезагрузите компьютер и убедитесь, что Linux загружается правильно.
Alert! /dev/ТОМ Does Not Exist
Проблема с Fstab
При загрузке хоста Linux вы можете увидеть следующую ошибку:
ALERT! /dev/sda1 does not exist. Dropping to a shell.
Возможно, вы только что установили Linux или у вашего хоста возникли проблемы с fstab. Чаще всего проблема возникает при установке системы с USB-накопителя. Система может показывать ошибку любого тома. Как и в первом случае, мы должны загрузиться с загрузочного / аварийного носителя Linux и выполнить некоторые действия. Проверьте UUID диска с помощью этой команды:
sudo blkid
Система вернёт примерно следующее:
/dev/sda2: UUID="36cce3d5-cbdb-46f4-adbf-3f9aaa01d729" TYPE="ext4" PARTUUID="fea4dab1-4e12-4327-85c6-76ade18f64e1"
Здесь мы видим, что система должна загружаться с sda2, но на самом деле она пытается загрузиться с sda1.
Смонтируйте том в любой каталог, например:
sudo mount /dev/sda2 /mnt
Когда вы увидите /dev/sda2 в каталоге /mnt, найдите там файл /etc/fstab и измените строку, содержащую /dev/sda1, следующим образом:
UUID=36cce3d5-cbdb-46f4-adbf-3f9aaa01d729 / ext4 errors=remount-rw 0 1
Сохраните файл. Отмонтируйте том от /mnt и перезагрузитесь. Если проблема связана с неправильным именем тома, сервер загрузится.
Также вы можете решить эту проблему, загрузившись в аварийном режиме. Перемонтируйте корневой каталог как чтение / запись:
sudo mount -o remount,rw /
Затем измените fstab и перезапустите сервер.
Аппаратная проблема
На некоторых материнских платах порты SATA могут иметь случайные числа. Это также может вызвать ошибку, описанную в предыдущем разделе. Чтобы исправить это, вы должны отредактировать загрузчик grub.
Загрузитесь в аварийном режиме или с Live CD и отредактируйте файл /boot/grub/grub.cfg.
В строке, определяющей загрузочный раздел, например:
Linux /boot/vmlinuz-4.15.0-70-generic root=/dev/sda1 rw quiet elevator=noop fsck.repair=yes
замените путь к диску на его UUID:
Linux /boot/vmlinuz-4.15.0-70-generic root=UUID=36cce3d5-cbdb-46f4-adbf-3f9aaa01d729 ro quiet elevator=noop fsck.repair=yes

Связанные статьи:
- Ошибка «Initramfs unpacking failed: invalid magic at start of compressed archive» (РЕШЕНО) (100%)
- Как загрузиться в режиме восстановления или в аварийном режиме в Ubuntu 20.04 / 18.04 (60.4%)
- Ошибка «Cannot open access to console, the root account is locked» (РЕШЕНО) (60.4%)
- Как определить, почему Linux загружается в Emergency mode (60.4%)
- Как отремонтировать диск LVM с помощью fsck (60.4%)
- Какие файлы можно удалить при нехватке места на диске Linux (RANDOM — 50.6%)
Модифицируем процесс загрузки с помощью утилиты make-initrd
В этой статье мы хотим помочь глубже разобраться, как устроен процесс загрузки Linux, дать советы по реализации сложных сценариев загрузки Linux, а также познакомить с удобным и быстрым генератором initramfs образов — make-initrd.

Упрощенный процесс загрузки ОС Linux
Загрузка ОС на устройстве с UEFI обычно проходит по следующему сценарию:
- Инициализация оборудования;
- Размещение в оперативной памяти EFI загрузчика (например, grub) и передача управления ему;
- EFI загрузчик записывает образ ядра в оперативную память и вызывает функцию start_kernel;
- Ядро инициализирует себя, монтирует корень файловой системы и запускает процесс init, лежащий на корне (/init, /sbin/init или другой). Этим процессом может быть systemd, System V и другие;
- Процесс init запускает сервисы и уже подготавливает систему для работы с пользователем.
Такая схема является вполне работоспособной. Но что, если мы хотим усложнить сценарий загрузки? Например, загрузиться с зашифрованного раздела или сетевого диска или, элементарно, проверить файловую систему перед загрузкой. На помощь приходит initramfs!
Что такое initramfs?
Initramfs (Initial ram file system) – образ файловой системы, загружаемый в оперативную память вместе с ядром. Основные задачи этого образа: монтирование реального корня файловой системы и запуск процесса init лежащего на нем.
При загрузке с initramfs ядро передает управление программе /init , лежащей в этом образе, а не в реальном корне. Разные initramfs образы могут иметь разную архитектуру: некоторые отрабатывают по фиксированному сценарию, а некоторые создают сложную инфраструктуру сервисов, работающих параллельно и взаимодействующие между собой.
Многие современные дистрибутивы Linux используют initramfs образы для загрузки системы. Вы можете убедиться в этом сами, найдя файл /boot/* в своем любимом дистрибутиве.
Возможные сценарии использования initramfs
Внутри initramfs образа могут реализовываться разнообразные сценарии загрузки ОС. Все они ограничены лишь возможностями Linux, характеристиками устройства и вашей фантазией.
Достаточно популярными и широко используемыми сценариями являются:
- Вывод приветственной картинки на экран;
- Проверка целостности корневой файловой системы;
- Загрузка с нестандартных файловых систем;
- Загрузка с LVM;
- Загрузка с RAID.
К экзотическим можно отнести:
- Загрузку с образа корня, лежащего в интернете;
- Загрузку с зашифрованного раздела;
- Загрузку другой ОС с помощью системного вызова kexec.
Утилиты для создания initramfs образов
Разные дистрибутивы используют разные утилиты для создания initrd образов. Debian обычно используют для этого initramfs-tools, Red Hat – Dracut. Но в этой статье я хочу представить другую, менее известную утилиту – make-initrd.
Почему make-initrd?
make-initrd – утилита для создания initramfs образов, разрабатываемая нашим соотечественником Алексеем Гладковым. Сейчас она используется как основной сборщик initramfs в дистрибутиве ALT Linux.
Этот проект обладает рядом преимуществ:
- Имеет модульную структуру. Разные логические части образа создаются разными модулями, именуемыми Features (фичами). Например, есть фича luks, отвечающая за внедрение сценария загрузки с зашифрованных разделов LUKS.
- Создаваемые initramfs образы имеют простое внутреннее устройство.
- Удобен для использования конечным пользователем. Многие части конфигурации определяются автоматически. Это достигается за счет отдельного модуля guess.
- Утилита показала свою работоспособность на множестве различных дистрибутивов. Нам лично удавалось собрать и использовать эту утилиту на Ubuntu, Fedora, ALT Linux, Red OS, Astra Linux.
- Генерируемые initramfs образы имеют относительно небольшой размер, а их сборка происходит достаточно быстро Для сравнения: initramfs-tools на моей машине генерирует образы размером 51Мб за 24 секунды. Образы же make-initrd весят всего 5Мб и создаются 10 секунд!
- Проект продолжает активно развиваться. Со временем появляются новые фичи, улучшается архитектура утилиты и архитектура initramfs образов. Разработчик идет на контакт и всегда рад вашим патчам и предложениям:)
К сожалению, у make-initrd есть и недостатки:
- Не самая полная и достовернаядокументация;
- Проект тестируется лишь на Fedora, Ubuntu и Alt Linux. И, хотя нам удавалось использовать его на других дистрибутивах, нет гарантии, что все будет работать гладко и везде.
Устройство initramfs образов, генерируемых make-initrd
В этом разделе описаны основные сведения по архитектуре образов, генерируемых make-initrd.
Сервисы
Создаваемые образы имеют сервисно-ориентированную архитектуру. При передаче управления программе /init порождается дерево сервисов, которые в дальнейшем взаимодействуют между собой. Последовательность их запуска и завершения определяется в процессе сборки образа и настраивается с помощью конфигурационных файлов LSB.
В системе есть несколько основных сервисов, на которых базируется основная логика работы:
- udevd – обработчик событий ядра об изменении состояния устройств. При изменении состояния любого устройства он параллельно запускает фильтры (скрипты), добавляющие пользовательские события в очередь. Этот демон используется во многих дистрибутивах Linux.
- ueventd – обработчик пользовательских событий. Этот демон запускает обработчики пользовательских событий. События внутри одной очереди обрабатываются последовательно, а сами очереди – параллельно.
- polld – демон, ожидающий выполнения условий загрузки системы. Все, что он делает – один раз в заданное время проверяет условия запуска системы. Если они удовлетворены, запускается некоторый экшен. Скрипты проверки условий и экшена зависят от метода загрузки. Тем не менее, зачастую, их задача – проверить, что корень найден и переключить runlevel на второй уровень.
Runlevel
Runlevel определяет уровень запуска системы. Эти уровни отличаются от тех, что стандартно используются в Linux. В make-initrd номер уровня зависит от состояния загрузки системы. А от номера уровня, в свою очередь, зависит то, какие сервисы будут активны в текущий момент. В make-initrd предусмотрено всего 3 уровня:
- Уровень 3 – означает, что реальный корень системы еще не примонтирован. На этом уровне происходит запуск и работа большинства сервисов.
- Уровень 2 – означает, что реальный корень системы уже примонтирован. На этом происходит завершение большинства сервисов.
- Уровень 9 – специальный уровень, на котором происходит подмена корня системы на реальный и запуск /sbin/init на нем.
Разберем пример загрузки с жесткого диска.
- После запуска /init , произойдет инициализация окружения: примонтируется файловая система procfs и установится runlevel=3;
- Последовательно запускаются различные сервисы, среди которых будут udevd, ueventd и polld;
- После обнаружения устройства жесткого диска ядро сгенерирует соответствующее событие для демона udevd;
- udevd просмотрит список своих правил. При нахождении правила на добавление блочного устройства запустится соответствующий обработчик (в make-initrd они называются фильтрами);
- Внутри фильтра произойдет проверка, что устройство описано в /etc/fstab . Если это так, сгенерируется пользовательское событие на примонтирование этого устройства;
- В какой-то момент ueventd запустит обработчик этого пользовательского события. Обработчик примонтирует жесткий диск;
- Демон polld в очередной раз запустит скрипт проверки наличия корня. После его обнаружения запустится экшен, переключающий runlevel на второй уровень;
- После переключения runlevel произойдет отключение большинства сервисов. Далее запустится сервис runinit, который установит runlevel на девятый уровень;
- Процесс init произведет подмену корня и запустит /sbin/init ;
Устройство файловой системы initramfs образов
Initramfs образ, генерируемый make-initrd, имеет строгую структуру файловой системы. Поэтому при добавлении собственной фичи важно понимать, что и где должно лежать:
- /init – программа, которой передается управление после инициализации ядра;
- /bin/ , /usr/bin/ – директории программ;
- /sbin/ , /usr/sbin/ – директории сервисов;
- /etc/ – директория конфигураций;
- /etc/rc.d/init.d/ – директория LSB конфигураций сервисов;
- /etc/udev/rules.d/ – директория правил udevd;
- /etc/initrd/method – файл содержащий текущий метод загрузки;
- /etc/initrd/cmdline.d/ – директория описаний аргументов ядра;
- /lib/ , /usr/lib/ – директории библиотек;
- /lib/uevent/filters/ – директория фильтров uevent событий;
- /lib/uevent/handler/ – директория обработчиков uevent событий;
- /lib/initrd/boot/method/ – директория скриптов методов загрузки;
- /lib/initrd/boot/method/*/check – скрипт проверки условии активации метода загрузки;
- /lib/initrd/boot/method/*/action – скрипт активации метода загрузки;
- /lib/initrd/boot/scripts/ – директория подскриптов проверки условий метода загрузки;
- /.initrd/ – директория, которую могут использовать сервисы и программы для внутренней работы;
- /tmp/ – директория временных файлов.
Устройство генератора initramfs образов
Для организации действий по созданию initramfs образов make-initrd использует make . Поэтому весь процесс создания можно представить в виде последовательности выполняемых целей. Основными целями при создании образа являются:
- process-config – Считывание конфигурационного файла. Во время ее выполнения происходит последовательный запуск целей guess и genimage;
- guess – Угадывание конфигурационного файла;
- create – Наполнение корня initramfs образа;
- pack – Сортировка сервисов и упаковка initramfs образа в cpio архив. Зависит от create;
- install – Установка образа в загрузочную директорию. Зависит от pack;
- genimage – Вывод информационного сообщения о сборке образа. Зависит от install.
Процесс выполнения этих целей можно разбить на два больших этапа:
1. Угадывание дополнения к конфигурации (цель guess);
2. Сборка образа (цели create – genimage)
Этап угадывания конфигурации
Ручная конфигурация make-initrd — достаточно сложный процесс. Например, вам нужно указать модули ядра устройств, которые вы хотите использовать в initramfs. Также немаловажно правильно задать список используемых фич.
Неправильное задание конфигурации грозит тем, что initramfs образ может оказаться излишне «тяжелым», а загрузка ОС будет происходить медленнее или не происходить вовсе. Например, если вы активировали фичу network, но не используете сеть в процессе загрузки, это грозит вам лишними секундами на настройку сетевых интерфейсов.
Именно для решения этих проблем в make-initrd был добавлен модуль guess. Он помогает оптимально сгенерировать или дополнить существующую конфигурацию, основываясь на окружении компьютера.
Guess использует разные модули для определения разных сфер образа. Например:
- device – определяет модули ядра устройств;
- keyboard – определяет модули ядра для работы с клавиатурой;
- net – определяет модули ядра сетевых устройств;
- root – определяет какие разделы нужно примонтировать, а также модули ядра для работы с ними;
- smart-card – определяет конфигурацию фичи smart-card;
- common – простой универсальный модуль, который может быть использован внутри фич.
Этот список не является исчерпывающим: описание остальных модулей можно посмотреть в README.md файлах модулей guess.
Этап сборки initramfs образа
Во время выполнения цели create создается временная директория, в которой заполняется корень initramfs образа. Корень заполняется в соответствии с переданной конфигурацией. Туда кладется все необходимое: приложения, библиотеки, файлы конфигурации и т.д. Но это еще не все: на последующих шагах корень еще будет дополняться.
Во время выполнения цели pack происходит определение порядка запуска и остановки сервисов. Этот порядок будет отражен в именах файлов директории /etc/rc.d . Например, сервис /etc/rc3.d/S08:udev будет запущен восьмым при переходе в runlevel=3. Далее сформированная директория с корнем упаковывается в cpio архив, в который дополнительно помещаются специальные файлы: /dev/ram , /dev/null , /dev/zero и т.д.
Во время выполнения цели install полученный архив установится в директорию /boot .
Этап сборки образа может сколь угодно расширяться с помощью фич. Например, если активирована фича compress, то cpio архив будет дополнительно сжат с помощью одного из методов: gzip, lzma, bzip2 и т.п.
Фичи
Фича – это независимый модуль сборщика initramfs образов. Фичи могут воздействовать практически на любой этап сборки образа. Например:
- фича compress позволяет сжимать итоговый образ;
- фича luks добавляет в initramfs утилиты и модули для работы с luks;
- фича clean – очищает после себя рабочую директорию.
Такая высокая модульность достигается за счет того, что make-initrd для организации своих действий использует make .
Структура проекта make-initrd
Выгрузим проект make-initrd из репозитория и посмотрим на его содержимое
# Выгружаем исходники git clone https://github.com/osboot/make-initrd --recursive
Проект make-initrd очень хорошо структурирован. Давайте опишем, за что отвечают различные файлы и директории в проекте:
- data/ – постоянная часть корня initramfs образа. Файлы, находящиеся в ней, будут присутствовать внутри почти всех образов;
- datasrc/ – исходники утилит, устанавливаемые внутрь образа;
- mk/config.mk – Makefile c постоянной частью конфигурации make-initrd (переменные путей и т.п.);
- mk/functions.mk – Makefile с вспомогательными функциями make;
- mk/make-initrd.mk – основной Makefile описывающий правила сборки initramfs образа;
- tools/ – внутренние утилиты make-initrd;
- utils/ – исходники внешних утилит make-initrd;
- guess/ – директория guess модулей;
- guess/*/config.mk – Makefile с описанием конфигурации guess модуля (переменные модуля, правила его активации). Он подключается вне зависимости от того, активен ли guess модуль или нет;
- guess/*/rules.mk – Makefile с описанием правил отработки guess модуля. Он подключается, только если guess модуль активен;
- features/ – директория фич;
- features/*/data/ – часть корня initramfs образа, предоставляемая фичей;
- features/*/datasrc/ – исходники утилит фичи, устанавливаемые внутрь initramfs;
- features/*/guess/ – подскрипты модулей guess;
- features/*/bin/ – вспомогательный утилиты сборки initramfs образа;
- features/*/config.mk – Makefile с конфигурацией фичи (описание зависимых фич, переменных и т.п.). Он подключается вне зависимости от того, активна ли фича или нет;
- features/*/rules.mk – Makefile с правилами сборки фичи. Он подключается только в случае, если фича активна.
Установка make-initrd
# Установим зависимые пакеты sudo apt-get install make automake pkg-config udev libkmod-dev libz-dev libbz2-dev liblzma-dev libzstd-dev libelf-dev libtirpc-dev libcrypt-dev help2man gcc opensc pcscd libpcsclite1 # Соберем проект ./autogen.sh ./configure make # Установим его sudo make install
Сборка initramfs и запуск на реальной машине
ПРЕДУПРЕЖДЕНИЕ Перед генерацией initramfs образа обязательно сделайте backup.
Если система перестанет загружаться, откройте раздел с /boot на другой системе (например, через загрузочную флешку) и верните backup на место. Если вы не сделали backup, попробуйте загрузиться с помощью другого ядра. Это может сработать, так как по-умолчанию make-initrd собирает initramfs только для текущего ядра.
# Backup текущего initramfs образа cp /boot/initrd.img-$(uname-r) /media/flash/initrd.backup # Запуск сборки sudo make-intird
Перезапустим систему и попробуем загрузиться c ядра, для которого мы сгенерировали initramfs образ.
Добавление собственной фичи
Чтобы лучше разобраться с тем, как устроены фичи, давайте напишем свою – hello-usb.
Фича Hello USB
Примечание Исходный код фичи hello-usb можно взять здесь.
Напишем фичу hello-usb, добавляющую в initramfs функционал вывода сообщения “Hello USB!” при нахождении USB-устройства. Также добавим возможность указывать имя производителя устройства, для которого выводится сообщение.
Внутренняя логика работы нашей фичи, следующая:
- Контроль добавления нового USB-устройства осуществляется с помощью udev правила /etc/udev/rules.d/99-hello-usb.rules : при добавлении любого USB-устройства вызывается фильтр /lib/uevent/filter/hello-usb
- Внутри фильтра создается uevent событие hello-usb
- Это событие обрабатывает специальный обработчик /lib/uevent/handler/hello-usb , который выводит в консоль сообщение «Hello USB!». Вывод происходит, только если производитель устройства совпал с тем, что передан в аргументе ядра usb-vendor.
Дерево файлов фичи следующее:
feature/hello-usb/config.mk feature/hello-usb/rules.mk feature/hello-usb/data/etc/initrd/cmdline.d/hello-usb feature/hello-usb/data/etc/udev/rules.d/99-hello-usb.rules feature/hello-usb/data/lib/uevent/filters/hello-usb feature/hello-usb/data/lib/uevent/handlers/060-hello-usb
config.mk содержит описание фичи:
# Зависимость от фичи usb $(call feature-requires,usb) # Путь до директории корневых данных фичи hello-usb HELLO_USB_DATADIR ?= $(FEATURESDIR)/hello-usb/data
Через функцию feature-requires можно указывать зависимые фичи. Мы хотели получить модули ядра и udev-правила для работы с USB-устройствами, поэтому добавили в зависимости фичу usb.
rules.mk содержит правила сборки фичи:
# Вывод сообщения hello-usb-show-info: @$(MSG) "Hello USB feature is activated . " # Вывод сообщения происходит перед упаковкой образа pack: hello-usb-show-info # Запоминаем директорию, которую хотим использовать, как часть корня PUT_FEATURE_DIRS += $(HELLO_USB_DATADIR) # Также в initrd можность класть программы, библиотеки, директории и файлы с помощью PUT_FEATURE_PROGS, PUT_FEATURE_LIBS, PUT_FEATURE_FILES, соответственно
feature/hello-usb/etc/initrd/cmdline.d/hello-usb – регистрирует опцию ядра usb-vendor:
register_parameter string USB_VENDOR
Помимо регистрации единичного аргумента можно зарегистрировать массив ( register_array ). Допустимо использовать аргументы типа: string , bool , number .
feature/hello-usb/etc/udev/rules.d/99-hello-usb.rules – udev правило, запускающее фильтр. Число в начале имени нужно для задания порядка его обработки. В нашем случае было важно, чтобы правило hello-usb отработало позже, чем 60-fido-id (т.к. оно выставляет тег security-device ). Поэтому мы выставили любое число больше 60 – это 99.
SUBSYSTEM=="usb", ENV=="usb_device", ACTION=="add", RUN+="/lib/uevent/filters/hello-usb"
Подробнее о udev правилах можно прочитать здесь.
#!/bin/bash . uevent-sh-functions # Создаем событие добавления USB-устройства event="$(make_event)" # Событие -- это файл хранящий переменные внутри себя echo "VENDOR=$ID_VENDOR" >> "$event" release_event hello-usb "$event"
Обратите внимание на простоту этого скрипта. Это связано с тем, что udev может запустить несколько таких скриптов одновременно, а это может привести к состоянию гонки. Именно поэтому логика большинства фильтров крайне проста – передать событие uevent, который обработает его последовательно.
feature/hello-usb/lib/uevent/handlers/060-hello-usb – обработчик события hello-usb. Uevent передает управление обработчикам в соотвествии с их номером. В нашем случае порядок обработки нашего события не важен, и мы выставили случайный номер – 60.
#!/bin/bash . /.initrd/initenv . uevent-sh-functions . initrd-sh-functions . rdshell-sh-functions # функция обработки вставки USB-устройства handler() < # проверяем совпадение имени производителя, если оно задано [ -n "$USB_VENDOR" ] && [ "$VENDOR" != "$USB_VENDOR" ] && return 0 echo "Hello USB!" return 0 ># Заблокируем вывод в консоль другим приложениям while ! console_lock; do sleep 0.5 done # Перенаправим I/O обработчика в консоль exec 0/dev/console 2>&1 rc=0 # Отфильтровываем события hello-usb for e in "$eventdir"/hello-usb.*; do [ -f "$e" ] || break r=0 # Запускаем функцию-обработчик с заданным окружением ( . "$e"; handler; ) || r="$?" case "$r" in # Событие остается в очереди 1) rc=1 ;; # Удаляем событие из очереди 0) done_event "$e" ;; esac done # Открываем доступ к консоли console_unlock exit $rc ``` Соберем make-initrd с добавленной фичей: ```bash make && sudo make install
Соберем make-initrd с добавленной фичей:
make && sudo make install
Настройка и генерация initrd образа
Сконфигурировать make-initrd можно через файл /etc/intird.mk . При генерации образа вы можете изменить путь до файла с конфигурацией, запустив make-initrd с опцией -с :
sudo make-initrd -c /path/to/config
Обычно файл с конфигурацией выглядит следующим образом:
$ cat /etc/initrd.mk # trying to detect modules and features to access to root volume AUTODETECT = all
Здесь написано, что вся конфигурация будет определена автоматически. Увидеть «угаданную» конфигурацию можно с помощью команды:
sudo make-initrd guess-config
Вы можете попросить make-initrd запускать только определенные guess модули. Например, можно попросить «угадывать» только модули ядра и фичи необходимые для монтирования корня и работы с клавиатурой:
AUTODETECT = root keyboard
Попробуем теперь собрать intird образ вместе с фичей hello-usb. Подправим конфигурацию
AUTODETECT = all FEATURES += hello-usb
И запустим сборку образа:
$ sudo make-initrd > # Считывается конфигурация > [00:00:00] Config file: /etc/initrd.mk > # Запускается guess модуль. Определяются недостающие части конфигурации > [00:00:01] Guessing config . > [00:00:01] Generating module dependencies on host . > [00:00:04] Guessing is complete > # Переход к шагу genimage > [00:00:04] Creating initrd image . > [00:00:06] Putting udev rules . > [00:00:06] Putting modules . > [00:00:06] Generating module dependencies in image … > # Выполнение rules.mk фичи hello-usb > [00:00:07] hello-usb feature is activated . > # Определяется порядок запуска и остановки сервисов > [00:00:07] Sorting sysvinit services … > # Упаковка образа в архив > [00:00:08] Packing image to archive . > [00:00:08] Writing build info files … > # Сжатие архива > [00:00:08] Compressing image . > [00:00:19] Adding CPU microcode … > # Список активированных фич > [00:00:19] Used features: add-modules add-udev-rules buildinfo cleanup compress depmod-image hello-usb network rdshell rootfs system-glibc ucode usb > # Список используемых модулей ядра > [00:00:19] Packed modules: ahci cqhci evbug hid hid-apple hid-generic input-leds libahci mac_hid sdhci sdhci-pci serio_raw uas usbhid usb-storage xhci-pci xhci-pci-renesas > # Установка образа в директорию /boot > [00:00:19] Installing image . > [00:00:19] Unpacked size: 47M > [00:00:19] Image size: 14M > [00:00:19] Removing work directory . > [00:00:19] Image is saved as /boot/initrd.img-5.11.0-31-generic
Перейдем к проверке нашего образа на виртуальной машине.
Запуск в qemu
Проверку образа будем производить с помощью qemu. Установим необходимые пакеты:
sudo apt-get install qemu-system-x86 qemu-kvm
В процессе работы мы планируем пробрасывать USB-устройство на виртуальную машину. Чтобы это сделать, необходимо узнать его адрес на USB-шине:
$ lsusb -t /: Bus 02.Port 1: Dev 1, Driver=xhci_hcd/6p, 5000M /: Bus 01.Port 1: Dev 1, Driver=xhci_hcd/12p, 480M |__ Port 1: Dev 2, If 0, Driver=, 480M |__ Port 2: Dev 14, If 0, Storage, Driver=usb-storage, 480M |__ Port 3: Dev 29, If 0, Specific Class, Driver=r8152, 480M |__ Port 5: Dev 4, If 0, Driver=uvcvideo, 480M |__ Port 5: Dev 4, If 1, Driver=uvcvideo, 480M |__ Port 8: Dev 5, If 1, Driver=btusb, 12M |__ Port 8: Dev 5, If 0, Driver=btusb, 12M
Мы хотим пробросить устройство, имеющее класс Mass Storage (флешку). В нашем случае оно находится на первой шине и втором порту.
Перейдем к запуску:
# Указываем путь до ядра # Указываем путь до initramfs образа # Указываем Размер оперативной памяти виртуальной машины в мегабайтах # Включаем KVM # Добавляем эмуляцию USB-контроллера # Указываем протокол работы контроллера (ehci. Для xhci передаем "-device qemu-xhci") # Указываем путь до пробрасываемемого устройства sudo qemu-system-x86_64 -kernel /boot/vmlinuz-$(uname -r) \ -initrd /boot/initrd.img-$(uname -r) \ -m 1024 \ -cpu host \ -enable-kvm \ -usb \ -device usb-ehci,id=ehci \ -device usb-host,hostbus=1,hostport=2
После запуска системы сначала отобразится вывод ядра, а затем вывод самого initrd. Если вы все сделали правильно, в какой-то момент увидите вывод обработчика событий hello-usb:
[ 1.461950] Run /init as init process [ 1.325924] INITRAMFS: version 2.22.0 [ 1.340645] INIT: Entering runlevel: 3 [ 1.357308] Starting shell service: [ DONE ] [ 1.430040] Parsing cmdline arguments: [ DONE ] [ 1.489825] Creating /etc/fstab: [ DONE ] [ 1.524020] Mounting filesystem [/dev]: [ DONE ] [ 1.554917] Mounting filesystem [/sys]: [ DONE ] [ 1.601215] Mounting filesystem [/run]: [ DONE ] [ 1.674068] Starting polld service: [ DONE ] [ 1.745135] Setting kernel runtime parameters: [ DONE ] [ 1.786626] Loading modules: [ DONE ] [ 1.824191] Starting udevd service: [ DONE ] [ 2.545714] Starting ueventd service: [ DONE ] [ 3.067812] Network up (lo): ` [ DONE ] Hello USB! Hello USB! Hello USB! Hello USB!
На этом, увы, ваша система зависнет:) Это связано с тем, что initramfs будет продолжать искать устройство с корнем, но оно не проброшено в qemu. К счастью, есть возможность экстренно перейти в rescue консоль: для этого надо нажать клавиши ** + **.
В ней можно изучить окружение initramfs образа:
* Убедитесь, что файловая структура, описанная ранее, соответствует действительности;
* Изучите список активных процессов;
* Посмотрите логи в /var/log/ ;
* Посмотрите содержимое других файлов.
Вы всегда можете запустить rescue консоль, если имеются какие-либо проблемы при запуске. В ней возможно:
- Изучить логи сервисов;
- Примонтировать корень вручную и запуститься с него;
- Запустить отладчик (если добавлена фича debug-tools).
Передача аргументов ядра
Для передачи аргументов ядра в qemu есть специальная опция -append . Укажем с помощью нее имя производителя USB-устройства, для которого мы хотим вывести сообщение “Hello USB!”. Его можно получить так:
# Узнаем номер USB-устройства -- значение Dev $ lsusb -t /: Bus 02.Port 1: Dev 1, Driver=xhci_hcd/6p, 5000M /: Bus 01.Port 1: Dev 1, Driver=xhci_hcd/12p, 480M |__ Port 1: Dev 2, If 0, Driver=, 480M |__ Port 2: Dev 14, If 0, Storage, Driver=usb-storage, 480M |__ Port 3: Dev 29, If 0, Specific Class, Driver=r8152, 480M |__ Port 5: Dev 4, If 0, Driver=uvcvideo, 480M |__ Port 5: Dev 4, If 1, Driver=uvcvideo, 480M |__ Port 8: Dev 5, If 1, Driver=btusb, 12M |__ Port 8: Dev 5, If 0, Driver=btusb, 12M # Номер шины $ BUS=001 # Номер устройства $ DEV=014 $ udevadm info --name /dev/bus/usb/$/$ | grep "ID_VENDOR bash">sudo qemu-system-x86_64 -kernel /boot/vmlinuz-$(uname -r) \ -initrd /boot/initrd.img-$(uname -r) \ -m 1024 \ -cpu host \ -enable-kvm \ -usb \ -device usb-ehci,id=ehci \ -device usb-host,hostbus=1,hostport=2 \ -append "usb-vendor=Aktiv"
Заметьте, если указать другое имя производителя, то в лог может ничего не вывестись.
Популярные опции ядра, которые поддерживает initramfs можно посмотреть здесь
Проверка на реальной машине
После того, как вы убедились, что initramfs образ работает в qemu, можно проверить его на реальной машине. Но здесь могут возникнуть некоторые нюансы.
Дело в том, что аргументы ядра, которые мы передавали в qemu с помощью опции -append , на реальной машине передаются не так просто. Обычно этим занимается UEFI загрузчик (GRUB, LILO rEFInd, nsh скрипт для UEFI Shell и т.д.) и настройка передаваемых опций ядра для каждого загрузчика производится по-разному.
Мы покажем, как это сделать, на примере GRUB.
Настройка аргументов ядра в GRUB
GRUB использует двухуровневую конфигурацию. Первый уровень – это файл /etc/default/grub (путь до этого файла дистрибутивозависим). На основании этого файла генерируется конфигурация второго уровня – /boot/grub/ , которую и используется непосредственно grub.
Откроем файл /etc/default/grub. Его содержимое выглядит примерно следующим образом:
# Номер пункта в меню загрузки, загружаемый по-умолчанию GRUB_DEFAULT=0 # Что выводить во время обратного отсчета GRUB_TIMEOUT_STYLE=hidden # Количество секунд перед загрузкой по-умолчанию GRUB_TIMEOUT=10 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` # Аргументы ядра используемые при нормальной загрузке GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" # Аргументы ядра, используемые при нормальной загрузке и в recovery mode GRUB_CMDLINE_LINUX=""
Подробнее об опциях grub можно прочитать здесь
Для генерации конфигурации второго уровня выполним команду:
sudo update-grub
Разбор полученной конфигурации является уже более сложной задачей и выходит за рамки этой статьи. Тем не менее посмотреть на файл /boot/grub/grub.cfg все равно будет полезно:) Подробную информацию о его конфигурации grub можно найти здесь.
Добавим в аргументы ядра, параметр usb-vendor. Для этого зададим в файле /etc/default/grub переменную GRUB_CMDLINE_LINUX_DEFAULT :
GRUB_CMDLINE_LINUX_DEFAULT="nosplash usb-vendor=Aktiv"
И снова обновим конфигурацию:
sudo update-grub
Перезапустите систему и посмотрите на результат.
Вывод
Естественно, фича hello-usb не раскрывает всю мощь использования make-initrd. Если вы хотите рассмотреть более живые примеры фич, посмотрите исходники фич:
- sshfsroot. – фича для загрузки с сетевых дисков по протоколу ssh;
- luks – фича для загрузки с зашифрованных разделов;
- raid – фича для загрузки с raid разделов.
В первые две, кстати, мы недавно добавили возможность для аутентификации по смарт-карте.
Обратите внимание насколько архитектура этих фичей совпадает с той, что мы реализовывали в hello-usb. Такая структурированность упрощает процесс разработки, и вы можете опираться на нее.
Make-initrd также удобен и для конечного использования. Такая крупная ОС, как ALT Linux, использует его в своих дистрибутивах в качестве основного генератора initramfs образов, в частности на таких продуктах, как “Альт Рабочая станция” и “Альт Сервер”. Это ли не показывает его широкую направленность?
На десктопных платформах вы можете добиться желаемого с помощью пары строк в конфигурационном файле, а порой полностью автоматически. Хотите добавить загрузку c сетевого раздела, используя sshfs? Просто напишите FEATURE += sshfsroot в файле конфигурации, а в опциях ядра укажите адрес сервера. Хотите добавить загрузку с RAID или LVM разделов? Ничего делать не надо! Если ваш корень лежит на таком разделе, make-initrd автоматически подберет правильную конфигурацию.
На серверных решениях вы можете добиться быстрой загрузки за счет высокой конфигурируемости образов. Вы всегда можете отключить любые ненужные вам фичи, тормозящие загрузку вашей системы. За счет обилия имеющегося функционала можно реализовать загрузку практически любой сложности. А если какого-то функционала будет не хавать, можно быстро реализовать его самостоятельно.
- Блог компании «Актив»
- Настройка Linux
- Информационная безопасность
- Системное администрирование
Глава 5. initramfs
В этой главе мы рассмотрим зачем нам в действительности требуется initramfs и почему она важна в общем процессе запуска. Иы знаем, что initramfs загружается в память самим начальным загрузчиком, но мы пока не обсуждали как выделяется initramfs. Данная глава адресована этому. Мы также рассмотрим все этапы выделения, повторной сборки и персонализации initramfs. Затем мы рассмотрим собственно структуру initramfs, а также соответствующую последовательность запуска внутри initramfs.
Зачем требуется initramfs?
основная цель процедуры запуска состоит в представлении своему пользователю его собственных файлов, которые размещены в общей корневой файловой системе. Иными словами, именно для имеющегося ядра в обязанности входят поиск, монтирование и представление такой корневой файловой системы своему пользователю. Для достижения этой цели наше ядро обязано запустить соответствующий исполняемый файл systemd, который, опять же, располагается в корневой файловой системе своего пользователя. Теперь это становится задачей курицы и яйца. Для запуска процесса systemd прежде всего нам требуется смонтировать необходимую корневую файловую систему, а для монтирования такой корневой файловой системы нам придётся запустить systemd из его корневой файловой системы. К тому же, помимо этой реальной корневой файловой системы пользователи могут обладать файлами в некоторых прочих файловых системах, таких как NFS, CIFS и т.п., а этот перечень прочих файловых систем также внутри устанавливаемой корневой файловой системы ( /etc/fstab ).
Итак, для решения нашей задачи курицы и яйца, разработчики пришли к решению с названием initramfs (что означает «initial RAM filesystem», изначальная файловая система в оперативной памяти). initramfs является некой временной корневой файловой системой (внутри своей оперативной памяти), которая будет применяться для монтирования своей реальной корневой файловой системы (со своего жёсткого диска или из сетевой среды). Итак, вся цель initramfs заключается в монтировании корневой файловой системы пользователя с его HDD/ из сети. В идеале наше ядро обладает достаточной способностью для монтирования своей корневой файловой системы с диска в нём без initramfs, но в наши дни корневая файловая система пользователя может быть где угодно. Она может располагаться в RAID, в неком LVM или в каком- то устройстве со множеством путей. Может иметься некое число n файловых систем, таких как XFS, ext4, ext3, NFS и т.п.. Это может быть даже зашифрованная файловая система, например, LUKS. Итак, ядру практически невозможно встроить все эти ситуации в своём собственном исполняемом файле vmlinux . Позвольте мне представить в этом разделе некие ситуации из реальной жизни.
Допустим, наша корневая файловая система расположена в NFS и нет никакого понятия об initramfs. Это означает, что наше ядро обязано смонтировать необходимую корневую файловую систему пользователя в самом себе из NFS. В таком случае, наше ядро должно осуществить следующие задачи:
- поднять собственный первичный сетевой интерфейс.
- Вызвать некого клиента DHCP и получить со своего сервера DHCP некий IP адрес.
- Отыскать необходимый совместный ресурс NFS и связанный с ним сервер NFS.
- Смонтировать этот совместный ресурс NFS (свою корневую файловую систему).
Для осуществления этих этапов нашему ядру потребуется обладать такими исполняемыми файлами: NetworkManager , dhclient , mount и так далее.
Теперь, предположим, наша корневая файловая система находится на устройстве программного RAID. Тогда наше ядро обязано выполнить такие задачи:
- Изначально отыскать необходимые диски RAID при помощи mdadm —examine —scan .
- После определения тех базовых диском, на которые распространяется этот программный RAID, ему требуется собрать этот RAID при помощи mdadm —assemble —scan
- Для достижения этого нашему ядру требуется обладать исполняемыми файлами mount и mdadm , а также некоторыми файлами настройки своих устройств соответствующего программного RAID.
Теперь, допустим, наша корневая файловая система расположена в неком логическом томе. Тогда для нашего ядра требуется покончить со следующими задачами для самого себя:
- Отыскать необходимые физические тома при помощи pvs .
- Найти необходимую группу томов посредством vgscan , а затем активировать её через vgchange .
- Просканировать полученный LVS воспользовавшись lvscan .
- наконец, после того как необходимый root lv заполнен, смонтировать его в качестве корневой файловой системы.
- Для достижения всего этого нашему ядру потребуется иметь исполняемый файлы, подобные pvscan , pvs , lvscan , vgscan , lvs и vgchange .
Давайте предположим, что наша корневая файловая система находится на зашифрованном блочном устройстве. В этом случае нашему ядру придётся завершить следующие задачи:
- Получить некий пароль от самого пользователя и/ или со вставленного аппаратного маркера (такого как смарт- карта или некий ключ безопасности USB).
- Создать некую расшифрованную цель при помощи надлежащего устройства соответствия.
для получения всего этого нашему ядру необходимы исполняемые файлы, относящиеся к LUKS.
Для ядра не представляется возможным включать в себе возможности всех таких корневых файловых систем; следовательно, разработчики пришли к понятию initramfs, чья единственная цель состоит в монтировании необходимой корневой файловой системы.
Само ядро всё ещё способно выполнять все только что обсуждённые нами этапы. Например, если вы соберёте простую систему Linux командной строки из LFS, вам не понадобится монтировать некую корневую файловую систему, поскольку ядро само по себе достаточно в состоянии смонтировать необходимую файловую систему. Однако в тот момент, как вы попробуете добавить в неё GUI через BLFS, вам потребуется initramfs.
Итак, наше заключение состоит в том, что наше ядро способно смонтировать в самом себе необходимую корневую файловую систему, однако для этого подобному ядру придётся удерживать все обсуждавшиеся исполняемые файлы, библиотеки поддержки, файлы настройки и т.п. в своём файле vmlinuz . Это создаст большое число проблем.
- Это испортит основной повод исполняемого самого файла ядра.
- Исполняемый файл такого ядра будет гигантским в размере. Чем большим размером будет обладать исполняемый файл, тем сложнее его сопровождение.
- Такой громадный исполняемый файл сложный в поддержке, обновлении, совместном использовании и обработке в серверах (с точки зрения пакетов RPM).
- Этот подход не будет следовать правилу KISS (keep it simple, stupid — делай проще, тупица).
Инфраструктура
Чтобы разобраться со структурой initramfs, нам внечале нужно освоить три различные файловые системы.
ramfs
Для более простого понимания, мы будем сравнивать ramfs с соответствующим механизмом кэширования ядра. Linux обладает некой уникальной функциональной возможностью, носящей название страничного кэша (page cache). Всякий раз, когда вы выполняете любую транзакцию ввода/ вывода, он кэширует эти транзакции страницами. Кэширование страницами в памяти это всегда хорошо. Это будет экономить наши последующие транзакции ввода/ вывода. И всякий раз, когда наша система сталкивается с ситуацией недостаточности памяти, наше ядро просто избавляется в памяти от таких кэшированны страниц. ramfs в точности как наше кэширование памяти. Однако основная проблема с ramfs состоит в том, что она не обладает стоящим за нею хранилищем; следовательно, она не способна выгружать свои страницы (устройство подкачки — swap, это опять- таки устройтво храенния). Итак, очевидно, что наше ядро не будет способно освобождать эту память, ибо не существует места для сохранения этих страниц. Следовательно, ramfs будет продолжать расти и не способна на практике ограничивать свой размер. Что мы можем сделать для упрощения данного положения вещей, так это допускать запись в ramfs лишь пользователю root.
tmpfs
tmpfs в точности как ramfs, но с некоторыми дополнениями. Мы способны накладывать некий предел на размер tmpfs, чего мы не способны делать в ramfs. Кроме того, страницы tmpfs могут использовать пространство подкачки.
rootfs
rootfs это некая tmpfs которая выступает экземпляром ramfs. Основное преимущество rootfs заключается в том, что у вас нет возможности отмены её монтирования. Это происходит по той же самой причине, по которой вы не способны уничтожить свой процесс systemd .
initramfs использует ramfs в качестве файловой системы и то пространство, которое занимается в памяти со стороны initramfs будет высвобождено после монтирования необходимой корневой файловой системы пользователя.
# dmesg | grep Free [ 0.813330] Freeing SMP alternatives memory: 36K [ 3.675187] Freeing initrd memory: 32548K
Раньше, вместо initramfs Linux применял initrd (initial RAM disk), однако initrd теперь признан устаревшим, а следовательно мы лишь перечислим несколько важных моментов для его сопоставления с initramfs.
initrd
Будучи отформатированным/ рассматриваемым в качестве некого блочного устройства, это подразумевает что initrd не обладает возможностью масштабирования. Что означает, что псле того как вы привнеси в память initrd и рассматриваете его в качестве некого блочного устройства, вы не способны увеличивать или уменьшать его размер.
Мы впустую тратим некую оперативную память в кэше, поскольку initrd рассматривается в качестве блочного устройства, ибо само ядро Linux спроектировано для удержания содержимого своего блочного устройства в кэше для снижения транзакций ввода/ вывода. Короче говоря, нет необходимости в кэшировании содержимого initrd самим ядром, так ак оно уже в памяти.
initramfs
В initrd всегда будут иметься накладные расходы на драйверы его файловой системы и исполняемых файлов, таких как mke2fs . Подобная команда mke2fs применяется для создания файловых систем ext2/3/4. Это означает, что часть области оперативной памяти будет вначале форматироваться при помощи такой файловой системы ext2/3/4 при помощи mke2fs , а затем из неё будет извлекаться initrd , тогда как initramfs , так же как и tmpfs, которую вы способны наращивать или усекать на лету.
Не существует дублирования данных между блочными устройствами и кэшем.
Для применения тогда как initramfs в качестве корневой файловой системы, нашему ядру не требуется никакой драйвер или исполняемый файл, подобный mke2fs , поскольку тогда как достижение initramfs произойдёт выделением в основной памяти как таковой.
# ls -lh /boot/initramfs-5.3.7-301.fc31.x86_64.img -rw-------. 1 root root 32M Dec 9 10:19 /boot/initramfs-5.3.7-301.fc31.x86_64.img
Для просмотра содержимого initramfs мы можем воспользоваться инструментом lsinitrd , или же мы можем распаковать initramfs с помощью соответствующего инструментария skipcpio
#lsinitrd Image: /boot/initramfs-5.3.7-301.fc31.x86_64.img: 32M ======================================================================== Early CPIO image ======================================================================== drwxr-xr-x 3 root root 0 Jul 25 2019 . -rw-r--r-- 1 root root 2 Jul 25 2019 early_cpio drwxr-xr-x 3 root root 0 Jul 25 2019 kernel drwxr-xr-x 3 root root 0 Jul 25 2019 kernel/x86 drwxr-xr-x 2 root root 0 Jul 25 2019 kernel/x86/microcode -rw-r--r-- 1 root root 100352 Jul 25 2019 kernel/x86/microcode/GenuineIntel.bin ======================================================================== Version: dracut-049-27.git20181204.fc31.1 Arguments: -f dracut modules: bash systemd systemd-initrd nss-softokn i18n network-manager network ifcfg drm plymouth dm kernel-modules kernel-modules-extra kernel-network-modules lvm qemu qemu-net resume rootfs-block terminfo udev-rules dracut-systemd usrmount base fs-lib shutdown ======================================================================== drwxr-xr-x 12 root root 0 Jul 25 2019 . crw-r--r-- 1 root root 5, 1 Jul 25 2019 dev/console crw-r--r-- 1 root root 1, 11 Jul 25 2019 dev/kmsg crw-r--r-- 1 root root 1, 3 Jul 25 2019 dev/null crw-r--r-- 1 root root 1, 8 Jul 25 2019 dev/random crw-r--r-- 1 root root 1, 9 Jul 25 2019 dev/urandom lrwxrwxrwx 1 root root 7 Jul 25 2019 bin -> usr/bin drwxr-xr-x 2 root root 0 Jul 25 2019 dev drwxr-xr-x 11 root root 0 Jul 25 2019 etc drwxr-xr-x 2 root root 0 Jul 25 2019 etc/cmdline.d drwxr-xr-x 2 root root 0 Jul 25 2019 etc/conf.d -rw-r--r-- 1 root root 124 Jul 25 2019 etc/conf.d/systemd.conf -rw-r--r-- 1 root root 0 Jul 25 2019 etc/fstab.empty -rw-r--r-- 1 root root 240 Jul 25 2019 etc/group -rw-r--r-- 1 root root 22 Jul 25 2019 etc/hostname lrwxrwxrwx 1 root root 25 Jul 25 2019 etc/initrd-release -> ../usr/lib/initrd-release -rw-r--r-- 1 root root 8581 Jul 25 2019 etc/ld.so.cache -rw-r--r-- 1 root root 28 Jul 25 2019 etc/ld.so.conf drwxr-xr-x 2 root root 0 Jul 25 2019 etc/ld.so.conf.d -rw-r--r-- 1 root root 17 Jul 25 2019 etc/ld.so.conf.d/libiscsi-x86_64.conf -rw-rw-r-- 1 root root 19 Jul 25 2019 etc/locale.conf drwxr-xr-x 2 root root 0 Jul 25 2019 etc/lvm -rw-r--r-- 1 root root 102256 Jul 25 2019 etc/lvm/lvm.conf -rw-r--r-- 1 root root 2301 Jul 25 2019 etc/lvm/lvmlocal.conf -r--r--r-- 1 root root 33 Jul 25 2019 etc/machine-id drwxr-xr-x 2 root root 0 Jul 25 2019 etc/modprobe.d
Для распаковки имеющегося содержимого initramfs воспользуйтесь исполняемым файлом skipcpio из /usr/lib/dracut/skipcpio/ . Этот skipcpio предоставляется инструментарием dracut. Мы обсудим dracut в Главе 6.
#/usr/lib/dracut/skipcpio initramfs-5.3.7-301.fc31.x86_64.img | gunzip -c | cpio -idv
Когда вы взглянете на распакованное содержимое initramfs, вы будете удивлены, узнав, что он выглядит в точности как корневая файловая система пользователя. Обратите, пожалуйста, внимание не то, что мы распаковали initramfs в своём каталоге /root/boot .
# ls -lh /root/boot/ total 44K lrwxrwxrwx. 1 root root 7 Mar 26 18:03 bin -> usr/bin drwxr-xr-x. 2 root root 4.0K Mar 26 18:03 dev drwxr-xr-x. 11 root root 4.0K Mar 26 18:03 etc lrwxrwxrwx. 1 root root 23 Mar 26 18:03 init -> usr/lib/systemd/systemd lrwxrwxrwx. 1 root root 7 Mar 26 18:03 lib -> usr/lib lrwxrwxrwx. 1 root root 9 Mar 26 18:03 lib64 -> usr/lib64 drwxr-xr-x. 2 root root 4.0K Mar 26 18:03 proc drwxr-xr-x. 2 root root 4.0K Mar 26 18:03 root drwxr-xr-x. 2 root root 4.0K Mar 26 18:03 run lrwxrwxrwx. 1 root root 8 Mar 26 18:03 sbin -> usr/sbin -rwxr-xr-x. 1 root root 3.1K Mar 26 18:03 shutdown drwxr-xr-x. 2 root root 4.0K Mar 26 18:03 sys drwxr-xr-x. 2 root root 4.0K Mar 26 18:03 sysroot drwxr-xr-x. 2 root root 4.0K Mar 26 18:03 tmp drwxr-xr-x. 8 root root 4.0K Mar 26 18:03 usr drwxr-xr-x. 3 root root 4.0K Mar 26 18:03 var
Вы обнаружите каталоги, подобные bin , sbin , usr , etc , var , lib и lib64 , которые мы применяем чтобы наблюдать свою корневую файловую систему. Помимо этого вы отметите соответствующие виртуальные каталоги, такие как dev , run , proc , sys и т.п.. Итак, initramfs в очности подобна корневой файловой системе пользователя. Давайте изучим каждый из каталогов для лучшего понимания своей реализации initramfs.
Реализация initramfs
Теперь мы рассмотрим как само содержимое initrafs, так и то как именно организована initrafs. На протяжении данного раздела мы разберёмся с тем, что initramfs ни что иное как небольшая корневая файловая система.
bin
Обычные исполняемые файлы
Мы можем использовать все приводимые ниже исполняемые файлы системы, которая закончила свою процедуру запуска. Поскольку все эти исполняемые файлы доступны внутри initramfs при продолжающемся запуске данной системы, мы будем иметь возможность применения этих команд в момент данного запуска.
cat, chown, cp, dmesg, echo, grep, gzip, less, ln, mkdir, mv, ps, rm, sed, sleep, umount, uname, vi, loadkeys, kbd_mode, flock, tr, true, stty, mount, sort и т.п.
[root@fedorab boot]# ls -la bin/ total 7208 drwxr-xr-x. 2 root root 4096 Jan 10 12:01 . drwxr-xr-x. 8 root root 4096 Dec 19 14:30 .. -rwxr-xr-x. 1 root root 1237376 Dec 19 14:30 bash -rwxr-xr-x. 1 root root 50160 Dec 19 14:30 cat -rwxr-xr-x. 1 root root 82688 Dec 19 14:30 chown -rwxr-xr-x. 1 root root 177144 Dec 19 14:30 cp -rwxr-xr-x. 1 root root 89344 Dec 19 14:30 dmesg -rwxr-xr-x. 1 root root 2666 Dec 19 14:30 dracut-cmdline -rwxr-xr-x. 1 root root 422 Dec 19 14:30 dracut-cmdline-ask -rwxr-xr-x. 1 root root 1386 Dec 19 14:30 dracut-emergency -rwxr-xr-x. 1 root root 2151 Dec 19 14:30 dracut-initqueue -rwxr-xr-x. 1 root root 1056 Jan 10 12:01 dracut-mount -rwxr-xr-x. 1 root root 517 Dec 19 14:30 dracut-pre-mount -rwxr-xr-x. 1 root root 928 Dec 19 14:30 dracut-pre-pivot -rwxr-xr-x. 1 root root 482 Dec 19 14:30 dracut-pre-trigger -rwxr-xr-x. 1 root root 1417 Dec 19 14:30 dracut-pre-udev -rwxr-xr-x. 1 root root 45112 Dec 19 14:30 echo -rwxr-xr-x. 1 root root 76768 Dec 19 14:30 findmnt -rwxr-xr-x. 1 root root 38472 Dec 19 14:30 flock -rwxr-xr-x. 1 root root 173656 Dec 19 14:30 grep -rwxr-xr-x. 1 root root 107768 Dec 19 14:30 gzip -rwxr-xr-x. 1 root root 78112 Dec 19 14:30 journalctl -rwxr-xr-x. 1 root root 17248 Dec 19 14:30 kbd_mode -rwxr-xr-x. 1 root root 387504 Dec 19 14:30 kmod -rwxr-xr-x. 1 root root 192512 Dec 19 14:30 less -rwxr-xr-x. 1 root root 85992 Dec 19 14:30 ln -rwxr-xr-x. 1 root root 222616 Dec 19 14:30 loadkeys lrwxrwxrwx. 1 root root 4 Dec 19 14:30 loginctl -> true -rwxr-xr-x. 1 root root 158056 Dec 19 14:30 ls -rwxr-xr-x. 1 root root 99080 Dec 19 14:30 mkdir -rwxr-xr-x. 1 root root 80264 Dec 19 14:30 mkfifo -rwxr-xr-x. 1 root root 84560 Dec 19 14:30 mknod -rwsr-xr-x. 1 root root 58984 Dec 19 14:30 mount -rwxr-xr-x. 1 root root 169400 Dec 19 14:30 mv -rwxr-xr-x. 1 root root 50416 Dec 19 14:30 plymouth -rwxr-xr-x. 1 root root 143408 Dec 19 14:30 ps -rwxr-xr-x. 1 root root 60376 Dec 19 14:30 readlink -rwxr-xr-x. 1 root root 83856 Dec 19 14:30 rm -rwxr-xr-x. 1 root root 127192 Dec 19 14:30 sed -rwxr-xr-x. 1 root root 52272 Dec 19 14:30 setfont -rwxr-xr-x. 1 root root 16568 Dec 19 14:30 setsid lrwxrwxrwx. 1 root root 4 Dec 19 14:30 sh -> bash -rwxr-xr-x. 1 root root 46608 Dec 19 14:30 sleep -rwxr-xr-x. 1 root root 140672 Dec 19 14:30 sort -rwxr-xr-x. 1 root root 96312 Dec 19 14:30 stat -rwxr-xr-x. 1 root root 92576 Dec 19 14:30 stty -rwxr-xr-x. 1 root root 240384 Dec 19 14:30 systemctl -rwxr-xr-x. 1 root root 20792 Dec 19 14:30 systemd-cgls -rwxr-xr-x. 1 root root 19704 Dec 19 14:30 systemd-escape -rwxr-xr-x. 1 root root 62008 Dec 19 14:30 systemd-run -rwxr-xr-x. 1 root root 95168 Dec 19 14:30 systemd-tmpfiles -rwxr-xr-x. 1 root root 173752 Dec 19 14:30 teamd -rwxr-xr-x. 1 root root 58400 Dec 19 14:30 tr -rwxr-xr-x. 1 root root 45112 Dec 19 14:30 true -rwxr-xr-x. 1 root root 442552 Dec 19 14:30 udevadm -rwsr-xr-x. 1 root root 41912 Dec 19 14:30 umount -rwxr-xr-x. 1 root root 45120 Dec 19 14:30 uname -rwxr-xr-x. 1 root root 1353704 Dec 19 14:30 vi
Особые исполняемые файлы
bash
initramfs снабжает нас некой оболочкой на момент запуска.
mknod
Мы будем обладать способностью создания устройств. dracut применяет udev , некий движимый событиями инструмент, который будет запускать определённые программы, например, lvm , mdadm и т.д. при выполнении определённых правил udev . К примеру, когда выполнено соответствие неких правил udev , под /dev появятся тома хранения и файлы устройств сетевых карт.
kmod
Некий инструментарий управления имеющимися модулями ядра.
Сетевые исполняемые файлы
Существует доступным в bin лишь единственный исполняемый файл, относящийся к сетевым ресурсам и им выступает teamd (initramfs способен обрабатывать собранные в группу сетевые устройства).
Особые точки входа
В Главе 7 и в Главе 9 мы обсудим особые точки входа.
dracut-cmdline dracut-cmdline-ask dracut-emergency dracut -initqueue dracut-mount dracut -pre-pivot dracut - pre-trigger dracut -pre-udev
Исполняемые файлы Systemd
systemd
Это прародитель всех процессов, который заменил собой init . именно он является самым первым процессом, кторый запускается в том момент, когда мы входим в initramfs.
systemctl
Диспетчер службы systemd.
systemd-cgls
Будет перечислять существующие группы управления (cgroups, control groups).
systemd-escape
Преобразует получаемую строку в формат элемента systemd, также носящего название экранированного (escaped).
systemd-run
Способен запускать полученные программы в виде некой службы, но в переходном режиме.
systemd-tmpfiles
Создаёт, удаляет и вычищает не постоянные и временные файлы и катоалоги.
journalctl
Некий инструмент, который имеет дело с журналом systemd.
Sbin
Исполняемые файлы файловой системы и относящиеся к хранению программы.
blkid
Считать атрибуты устройства.
chroot
Изменить устройство корневой текущей файловой системы.
e2fsck
Проверить файловые системы ext2/3/4.
fsck, fsck.ext4
Проверить и восстановить соответствующую файловую систему.
swapoff
Когда вы желаете остановить имеющееся устройство подкачки.
dmsetup
Инструмент установки соответствия для упраовления LVM.
dmeventd
Демеон событий соответствия устройств.
lvm
Инструмент управления LVM, который предоставляет команды lvscan , vgscan , vgchange , pvs и т.п..
lvm_scan
Сценарий поиска устройств LVM.
Сетевые исполняемые файлы
dhclient
Для полусения IP от имеющегося сервера DHCP.
losetup
Для настройки устройства loop .
Netroot
Для поддержки root поверх установленной сетевой среды.
NetworkManager
Некий инструментарий управления имеющимися сетевыми устройствами.
Особые исполняемые файлы
depmod
Для генерации modules.dep (символической ссылки - symlink kmod ).
lsmod
Для просмотра загруженных модулей (символической ссылки - symlink kmod ).
modinfo
Для выдачи на печать сведений о модуле (символической ссылки - symlink kmod ).
modprobe
Для загрузки или вставки модулей (символической ссылки - symlink kmod ).
rmmod
Для удаления загруженного модуля (символической ссылки - symlink kmod ).
init / systemd
kexec
kexec ядра, используемый Kdump.
udevadm
Базовые исполняемые файлы
Наконец, вот имеющиеся базовые исполняемые файлы:
Halt, poweroff, reboot
[root@fedorab boot]# ls -lah sbin/ total 13M drwxr-xr-x. 2 root root 4.0K Dec 19 14:30 . drwxr-xr-x. 8 root root 4.0K Dec 19 14:30 .. -rwxr-xr-x. 1 root root 126K Dec 19 14:30 blkid -rwxr-xr-x. 1 root root 50K Dec 19 14:30 chroot lrwxrwxrwx. 1 root root 11 Dec 19 14:30 depmod -> ../bin/kmod -rwxr-xr-x. 1 root root 2.9M Dec 19 14:30 dhclient -r-xr-xr-x. 1 root root 45K Dec 19 14:30 dmeventd -r-xr-xr-x. 1 root root 159K Dec 19 14:30 dmsetup -rwxr-xr-x. 2 root root 340K Dec 19 14:30 e2fsck -rwxr-xr-x. 1 root root 58K Dec 19 14:30 fsck -rwxr-xr-x. 2 root root 340K Dec 19 14:30 fsck.ext4 lrwxrwxrwx. 1 root root 16 Dec 19 14:30 halt -> ../bin/systemctl lrwxrwxrwx. 1 root root 22 Dec 19 14:30 init -> ../lib/systemd/systemd -rwxr-xr-x. 1 root root 1.2K Dec 19 14:30 initqueue lrwxrwxrwx. 1 root root 11 Dec 19 14:30 insmod -> ../bin/kmod -rwxr-xr-x. 1 root root 197 Dec 19 14:30 insmodpost.sh -rwxr-xr-x. 1 root root 203K Dec 19 14:30 kexec -rwxr-xr-x. 1 root root 496 Dec 19 14:30 loginit -rwxr-xr-x. 1 root root 117K Dec 19 14:30 losetup lrwxrwxrwx. 1 root root 11 Dec 19 14:30 lsmod -> ../bin/kmod -r-xr-xr-x. 1 root root 2.4M Dec 19 14:30 lvm -rwxr-xr-x. 1 root root 3.5K Dec 19 14:30 lvm_scan lrwxrwxrwx. 1 root root 11 Dec 19 14:30 modinfo -> ../bin/kmod lrwxrwxrwx. 1 root root 11 Dec 19 14:30 modprobe -> ../bin/kmod -rwxr-xr-x. 1 root root 2.7K Dec 19 14:30 netroot -rwxr-xr-x. 1 root root 5.3M Dec 19 14:30 NetworkManager -rwxr-xr-x. 1 root root 16K Dec 19 14:30 nologin -rwxr-xr-x. 1 root root 150K Dec 19 14:30 plymouthd lrwxrwxrwx. 1 root root 16 Dec 19 14:30 poweroff -> ../bin/systemctl -rwxr-xr-x. 1 root root 1.4K Dec 19 14:30 rdsosreport lrwxrwxrwx. 1 root root 16 Dec 19 14:30 reboot -> ../bin/systemctl lrwxrwxrwx. 1 root root 11 Dec 19 14:30 rmmod -> ../bin/kmod -rwxr-xr-x. 1 root root 25K Dec 19 14:30 swapoff -rwxr-xr-x. 1 root root 6.0K Dec 19 14:30 tracekomem lrwxrwxrwx. 1 root root 14 Dec 19 14:30 udevadm -> ../bin/udevadm
Разве не удивительно, что не обладая реальной корневой файловой системой пользователя мы имеем возможность применять оболочку, сетевую среду, модули, устройства и т.п., а также управлять ими? Иными словами, вам и в самом деле не требуется корневая файловая система пользователя пока пользователь не пожелает получить досткп к своим частным файлам, но это просто шутка.
Теперь возникает вопрос: где и как мы можем применять эти команды? Эти исполняемые файлы и команды будут автоматически применяться initramfs. Или, говоря правильно, эти исполняемые файлы и команды мы будем применять systemd initramfs для монтирования реальной корневой файловой системы пользователя, но, если systemd завершит это отказом, он предоставит нам некую оболочку и мы будем иметь возможность применять эти команды и устранять впоследствии неисправности. Мы обсудим это в Главе 7, Главе 8 и Главе 9.
etc
Имеющиеся в каталогах bin и sbin исполняемые файлы будут иметь собственные файлы настроек и они будут храниться в соответствующем каталоге etc initramfs.
[root@fedorab boot]# tree etc/ etc/ ├── cmdline.d ├── conf.d │ └── systemd.conf ├── fstab.empty ├── group ├── hostname ├── initrd-release -> ../usr/lib/initrd-release ├── ld.so.cache ├── ld.so.conf ├── ld.so.conf.d │ └── libiscsi-x86_64.conf ├── locale.conf ├── lvm │ ├── lvm.conf │ └── lvmlocal.conf ├── machine-id ├── modprobe.d │ ├── firewalld-sysctls.conf │ ├── kvm.conf │ ├── lockd.conf │ ├── mlx4.conf │ ├── nvdimm-security.conf │ └── truescale.conf ├── mtab -> /proc/self/mounts ├── os-release -> initrd-release ├── passwd ├── plymouth │ └── plymouthd.conf ├── sysctl.conf ├── sysctl.d │ └── 99-sysctl.conf -> ../sysctl.conf ├── systemd │ ├── journald.conf │ └── system.conf ├── system-release -> ../usr/lib/fedora-release ├── udev │ ├── rules.d │ │ ├── 11-dm.rules │ │ ├── 59-persistent-storage-dm.rules │ │ ├── 59-persistent-storage.rules │ │ ├── 61-persistent-storage.rules │ │ └── 64-lvm.rules │ └── udev.conf ├── vconsole.conf └── virc 10 directories, 35 files
Виртуальные файловые системы
Виртуальные файловые системы являются определённым видом файловых систем, чьи файлы в реальности не присутствуют на диске; вместо этого данная файловая система целиком доступна в памяти. Это имеет свои собственные преимущества и недостатки; например, вы получаете очень высокую пропускную способность , однако эта файловая система не способна хранить свои данные на постоянной основе. Внутри initramfs существует три виртуальные файловые системы, которыми выступают dev , proc и sys . Здесь я дал некое краткое введение в эти файловые системы, но мы обсудим их подробнее своих последующих главах:
[root@fedorab boot]# ls -lah dev total 8.0K drwxr-xr-x. 2 root root 4.0K Dec 19 14:30 . drwxr-xr-x. 12 root root 4.0K Dec 19 14:33 .. crw-r--r--. 1 root root 5, 1 Dec 19 14:30 console crw-r--r--. 1 root root 1, 11 Dec 19 14:30 kmsg crw-r--r--. 1 root root 1, 3 Dec 19 14:30 null crw-r--r--. 1 root root 1, 8 Dec 19 14:30 random crw-r--r--. 1 root root 1, 9 Dec 19 14:30 urandom [root@fedorab boot]# ls -lah proc/ total 8.0K drwxr-xr-x. 2 root root 4.0K Dec 19 14:30 . drwxr-xr-x. 12 root root 4.0K Dec 19 14:33 .. [root@fedorab boot]# ls -lah sys/ total 8.0K drwxr-xr-x. 2 root root 4.0K Dec 19 14:30 . drwxr-xr-x. 12 root root 4.0K Dec 19 14:33 ..
dev
На данный момент существует лишь пять файлов устройств по умолчанию, но при запуске данной системы udev полностью запонит данный каталог. Файлы устройств console , kmsg , null , random и urandom будут создаваться самим ядром, или, иначе говоря, эти устройства создаются вручную при помощи команды mknod , однако все остальные файлы создаются udev .
proc и sys
Как только наше ядро получает контроль над выполняемой процедурой запуска, это ядро создаст и заполнит эти каталоги. Файловая система proc будет хранить все связанные с процессами сведения, например, /proc/1/status , в то время как sys будет хранить сведения об имеющихся устройствах и связанную с их драйверами, например, /sys/fs/ext4/sda5/errors_count .
usr, var
Как мы знаем, в эти дни usr это отдельная иерархия в общей корневой файловой системой. Наши /bin , /sbin , /lib и /lib64 являются ничем иным как всего лишь символическими ссылками на usr/bin , usr/sbin , usr/lib и usr/lib64 .
# ls -l bin lrwxrwxrwx. 1 root root 7 Dec 21 12:19 bin -> usr/bin # ls -l sbin lrwxrwxrwx. 1 root root 8 Dec 21 12:19 sbin -> usr/sbin # ls -la usr total 40 drwxr-xr-x. 8 root root 4096 Dec 21 12:19 . drwxr-xr-x. 12 root root 4096 Dec 21 12:19 .. drwxr-xr-x. 2 root root 4096 Dec 21 12:19 bin drwxr-xr-x. 12 root root 4096 Dec 21 12:19 lib drwxr-xr-x. 4 root root 12288 Dec 21 12:19 lib64 drwxr-xr-x. 2 root root 4096 Dec 21 12:19 libexec drwxr-xr-x. 2 root root 4096 Dec 21 12:19 sbin drwxr-xr-x. 5 root root 4096 Dec 21 12:19 share # ls -la var total 12 drwxr-xr-x. 3 root root 4096 Dec 21 12:19 . drwxr-xr-x. 12 root root 4096 Dec 21 12:19 .. lrwxrwxrwx. 1 root root 11 Dec 21 12:19 lock -> ../run/lock lrwxrwxrwx. 1 root root 6 Dec 21 12:19 run -> ../run drwxr-xr-x. 2 root root 4096 Dec 21 12:19 tmp
lib, lib64
Существует почти 200 библиотек и почти все они предоставляются glibc , например, libc.so.6
Каталоги lib и lib64 это всего лишь символические ссылки на usr/lib и usr/lib64 .
# ls -l lib lrwxrwxrwx. 1 root root 7 Dec 21 12:19 lib -> usr/lib # ls -l lib64 lrwxrwxrwx. 1 root root 9 Dec 21 12:19 lib64 -> usr/lib64 # ls -la lib/ total 128 drwxr-xr-x. 12 root root 4096 Dec 21 12:19 . drwxr-xr-x. 8 root root 4096 Dec 21 12:19 .. drwxr-xr-x. 3 root root 4096 Dec 21 12:19 dracut -rwxr-xr-x. 1 root root 34169 Dec 21 12:19 dracut-lib.sh -rw-r--r--. 1 root root 31 Dec 21 12:19 fedora-release drwxr-xr-x. 6 root root 4096 Dec 21 12:19 firmware -rwxr-xr-x. 1 root root 6400 Dec 21 12:19 fs-lib.sh -rw-r--r--. 1 root root 238 Dec 21 12:19 initrd-release drwxr-xr-x. 6 root root 4096 Dec 21 12:19 kbd drwxr-xr-x. 2 root root 4096 Dec 21 12:19 modprobe.d drwxr-xr-x. 3 root root 4096 Dec 21 12:19 modules drwxr-xr-x. 2 root root 4096 Dec 21 12:19 modules-load.d -rwxr-xr-x. 1 root root 25295 Dec 21 12:19 net-lib.sh lrwxrwxrwx. 1 root root 14 Dec 21 12:19 os-release -> initrd-release drwxr-xr-x. 2 root root 4096 Dec 21 12:19 sysctl.d drwxr-xr-x. 5 root root 4096 Dec 21 12:19 systemd drwxr-xr-x. 2 root root 4096 Dec 21 12:19 tmpfiles.d drwxr-xr-x. 3 root root 4096 Dec 21 12:19 udev # ls -la lib64/libc.so.6 lrwxrwxrwx. 1 root root 12 Dec 21 12:19 lib64/libc.so.6 -> libc-2.30.so # dnf whatprovides lib64/libc.so.6 glibc-2.30-5.fc31.x86_64 : The GNU libc libraries Repo : @System Matched from: Filename : /lib64/libc.so.6
Загрузка initramfs
Достаточно просто разобраться в самом основном потоке последовательности запуска initramfs:
- Поскольку initramfs вступает некой корневой файловой системой ( temporary ), она создаст то окружение, которое требуется для запуска его процессов. initramfs будет смонтирована в качестве корневой файловой системы (временный / ) и из неё будут запущены такие программы как systemd.
- После этого внутри временного каталога initramfs монтируется корневая файловая система некого нового пользователя с вашего HDD, либо из сетевой среды.
- После того как внутри initramfs смонтирована корневая файловая система этого пользователя, имеющееся ядро запустит установленный исполняемый файл init , который не что иное как символическая ссылка на systemd , самый первый процесс в нашей операционной системе.
# ls init -l lrwxrwxrwx. 1 root root 23 Dec 21 12:19 init -> usr/lib/systemd/systemd
Мы можем перепроверить на самом ли деле наше ядро запускает соответствующий процесс init/systemd сразу после раскрытия initramfs. Для этого мы можем видоизменить свой сценарий init , однако основным препятствием в этом выступает то, что systemd является исполняемым файлом, в то время как init применяется как некий сценарий. Мы запросто можем отредактировать init , поскольку он является файлом сценария, однако мы не способны видоизменять исполняемый файл systemd . Тем не менее, для хорошего понимания и проверки нашей последовательности запуска на предмет того, что systemd вызывается сразу после того, как имеющееся ядро распаковывает initramfs, мы применим систему н основе init . Это был бы достойный пример, ибо systemd в данном месте служит в качестве замены системе init . Кроме того, init всё ещё выступает символической ссылкой на systemd . Мы воспользуемся системой Centos 6, которая является дистрибутивом Linux на основе init .
Прежде всего выделяем initramfs.
# zcat initramfs-2.6.32-573.el6.x86_64.img | cpio –idv [root@localhost initramfs]# ls -lah total 120K drwxr-xr-x. 26 root root 4.0K Mar 27 12:56 . drwxr-xr-x. 3 root root 4.0K Mar 27 12:56 .. drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 bin drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 cmdline drwxr-xr-x. 3 root root 4.0K Mar 27 12:56 dev -rw-r--r--. 1 root root 19 Mar 27 12:56 dracut-004-388.el6 drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 emergency drwxr-xr-x. 8 root root 4.0K Mar 27 12:56 etc -rwxr-xr-x. 1 root root 8.8K Mar 27 12:56 init drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 initqueue drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 initqueue-finished drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 initqueue-settled drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 initqueue-timeout drwxr-xr-x. 7 root root 4.0K Mar 27 12:56 lib drwxr-xr-x. 3 root root 4.0K Mar 27 12:56 lib64 drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 mount drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 netroot drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 pre-mount drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 pre-pivot drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 pre-trigger drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 pre-udev drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 proc drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 sbin drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 sys drwxr-xr-x. 2 root root 4.0K Mar 27 12:56 sysroot drwxrwxrwt. 2 root root 4.0K Mar 27 12:56 tmp drwxr-xr-x. 8 root root 4.0K Mar 27 12:56 usr drwxr-xr-x. 4 root root 4.0K Mar 27 12:56 var
Открываем файл init и добавляем в него следующий баннер:
#vim init "We are inside the init process. Init is replaced by Systemd" #!/bin/sh # # Licensed under the GPLv2 # # Copyright 2008-2009, Red Hat, Inc. # Harald Hoyer # Jeremy Katz echo "we are inside the init process. Init is replaced by Systemd" wait_for_loginit() < if getarg rdinitdebug; then set +x exec 0<>/dev/console 1<>/dev/console 2<>/dev/console # wait for loginit i=0 while [ $i -lt 10 ]; do . . .
>
Повторно упакуем initramfs с названием test.img .
[root@localhost initramfs]# find . | cpio -o -c | gzip -9 > /boot/test.img 163584 blocks # ls -lh /boot/ total 66M -rw-r--r--. 1 root root 105K Jul 23 2015 config-2.6.32-573.el6.x86_64 drwxr-xr-x. 3 root root 1.0K Aug 7 2015 efi -rw-r--r--. 1 root root 163K Jul 20 2011 elf-memtest86+-4.10 drwxr-xr-x. 2 root root 1.0K Dec 21 16:12 grub -rw-------. 1 root root 27M Dec 21 15:55 initramfs-2.6.32-573.el6.x86_64.img -rw-------. 1 root root 5.3M Dec 21 16:03 initrd-2.6.32-573.el6.x86_64kdump.img drwx------. 2 root root 12K Dec 21 15:54 lost+found -rw-r--r--. 1 root root 162K Jul 20 2011 memtest86+-4.10 -rw-r--r--. 1 root root 202K Jul 23 2015 symvers-2.6.32-573.el6.x86_64.gz -rw-r--r--. 1 root root 2.5M Jul 23 2015 System.map-2.6.32-573.el6.x86_64 -rw-r--r--. 1 root root 27M Mar 27 13:16 test.img -rwxr-xr-x. 1 root root 4.1M Jul 23 2015 vmlinuz-2.6.32-573.el6.x86_64
Запустим новый initramfs test.img и вы заметите что сразу после распаковки initramfs был выведен на печать наш баннер.
. . . cpuidle: using governor ladder cpuidle: using governor menu EFI Variables Facility v0.08 2004-May-17 usbcore: registered new interface driver hiddev usbcore: registered new interface driver usbhid usbhid: v2.6:USB HID core driver GRE over IPv4 demultiplexor driver TCP cubic registered Initializing XFRM netlink socket NET: Registered protocol family 17 registered taskstats version 1 rtc_cmos 00:01: setting system clock to 2020-03-27 07:53:44 UTC (1585295624) Initalizing network drop monitor service Freeing unused kernel memory: 1296k freed Write protecting the kernel read-only data: 10240k Freeing unused kernel memory: 732k freed Freeing unused kernel memory: 1576k freed we are inside the init process. Init is replaced by Systemd dracut: dracut-004-388.el6 dracut: rd_NO_LUKS: removing cryptoluks activation device-mapper: uevent: version 1.0.3 device-mapper: ioctl: 4.29.0-ioctl (2014-10-28) initialised: dm-devel@redhat.com udev: starting version 147 dracut: Starting plymouth daemon . .
Как ядро выделяет initramfs из памяти?
Задержимся на минутку чтобы попытаться восстановить всё что мы изучили на данный момент.
- Самым первым запускается начальный загрузчик.
- Этот начальный загрузчик копирует в память необходимые ядро и initramfs.
- Ядро распаковывает себся самостоятельно.
- Наш начальный загрузчик передал значение местоположения initramfs в устанавливаемое ядро.
- Это ядро распаковывает в памяти initramfs.
- Из распакованного initramfs наше ядро запускает systemd.
Собственно распаковка происходит в файле ядра init/initramfs.c . За это выделение ответственна функция populate_rootfs .
populate_rootfs function: . . 646 static int __init populate_rootfs(void) 647 < 648 /* Load the built in initramfs */ 649 char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size); 650 if (err) 651 panic("%s", err); /* Failed to decompress INTERNAL initramfs */ 652 653 if (!initrd_start || IS_ENABLED(CONFIG_INITRAMFS_FORCE)) 654 goto done; 655 656 if (IS_ENABLED(CONFIG_BLK_DEV_RAM)) 657 printk(KERN_INFO "Trying to unpack rootfs image as initramfs. \n"); 658 else 659 printk(KERN_INFO "Unpacking initramfs. \n"); 660 661 err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start); 662 if (err) < 663 clean_rootfs(); 664 populate_initrd_image(err); 665 >666 667 done: 668 /* 669 * If the initrd region is overlapped with crashkernel reserved region, 670 * free only memory that is not part of crashkernel region. 671 */ 672 if (!do_retain_initrd && initrd_start && !kexec_free_initrd()) 673 free_initrd_mem(initrd_start, initrd_end); 674 initrd_start = 0; 675 initrd_end = 0; 676 677 flush_delayed_fput(); 678 return 0; 679 > . . unpack_to_rootfs function: . . 443 static char * __init unpack_to_rootfs(char *buf, unsigned long len) 444 < 445 long written; 446 decompress_fn decompress; 447 const char *compress_name; 448 static __initdata char msg_buf[64]; 449 450 header_buf = kmalloc(110, GFP_KERNEL); 451 symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL); 452 name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL); 453 454 if (!header_buf || !symlink_buf || !name_buf) 455 panic("can't allocate buffers"); 456 457 state = Start; 458 this_header = 0; 459 message = NULL; 460 while (!message && len) < 461 loff_t saved_offset = this_header; 462 if (*buf == '0' && !(this_header & 3)) < 463 state = Start; 464 written = write_buffer(buf, len); 465 buf += written; 466 len -= written; 467 continue; 468 >469 if (!*buf) < 470 buf++; 471 len--; 472 this_header++; 473 continue; 474 >475 this_header = 0; 476 decompress = decompress_method(buf, len, &compress_name); 477 pr_debug("Detected %s compressed data\n", compress_name); 478 if (decompress) < 479 int res = decompress(buf, len, NULL, flush_buffer, NULL, 480 &my_inptr, error); 481 if (res) 482 error("decompressor failed"); 483 >else if (compress_name) < 484 if (!message) < 485 snprintf(msg_buf, sizeof msg_buf, 486 "compression method %s not configured", 487 compress_name); 488 message = msg_buf; 489 >490 > else 491 error("invalid magic at start of compressed archive"); 492 if (state != Reset) 493 error("junk at the end of compressed archive"); 494 this_header = saved_offset + my_inptr; 495 buf += my_inptr; 496 len -= my_inptr; 497 > 498 dir_utime(); 499 kfree(name_buf); 500 kfree(symlink_buf); 501 kfree(header_buf); 502 return message; 503 > . .
Внутри функции populate_rootfs имеется функция unpack_to_rootfs . Именно эта функция выступает исполнителем распаковки initramfs и она возвращает 0 в случае отказа и 1 в случае успеха. Кроме того обратите внимание на занимательные параметры этой функции:
- __initramfs_start : Это значение точного местоположения/ адреса загруженной initramfs (initramfs будет загружаться соответствующим начальным загрузчиком, а потому очевидно, что значение адреса местоположения также предоставляется этим начальным загрузчиком через boot_protocol ).
- __initramfs_size : Это значение размера образа загруженного initramfs.
Как ядро монтирует initramfs в качестве корня?
Большой двоичный объект (blob) initramfs это просто некий файл cpio (возможно сжатый). Наше ядро распаковывает его создавая в памяти некую файловую систему tmpfs/ramfs. Поэтому на практике не существует некого фиксированного местоположения; Выделение памяти по мере распаковки выполняет само ядро. Мы уже отмечали, что наш начальный загрузчик GRUB 2 помещает загружаемое ядро в определённом местоположении, которое будет зависеть от архитектуры, однако распаковка образа initramfs не производится в каком- то определённом месте.
Теперь, прежде чем мы мы проследуем далее в своей последовательности запуска , нам требуется разобраться с имеющимся инструментарием dracut, который создаётся initramfs. Этот инструментарий снабдит нас лучшим пониманием initramfs и systemd.