Что содержит образ initrd в linux
Для использования /dev/initrd с поддержкой «диска в RAM» и «начального диска в RAM» ядро Linux должно быть собрано с параметрами CONFIG_BLK_DEV_RAM=y и CONFIG_BLK_DEV_INITRD=y. При использовании /dev/initrd драйвер диска в RAM не может загружаться как модуль.
ОПИСАНИЕ
Во время первой фазы ядро запускается и монтирует начальную корневую файловую систему из содержимого /dev/initrd (например, RAM-диска, инициализированного системным загрузчиком). Во время второй фазы из начального содержимого корневого устройства загружаются дополнительные драйверы и модули. После загрузки дополнительных модулей с другого устройства монтируется новая корневая файловая система (т.е., нормальная корневая файловая система).
Выполнение загрузки
Параметры
Смена нормальной корневой файловой системы
Также сменить нормальное корневое устройство можно из /linuxrc. Для этого должен быть смонтирован каталог /proc. После монтирования /proc, сменить нормальное корневое устройство из /linuxrc можно записав настройки в proc-файлы /proc/sys/kernel/real-root-dev, /proc/sys/kernel/nfs-root-name и /proc/sys/kernel/nfs-root-addrs. Для смены физического корневого устройства из /linuxrc нужно записать номер нового устройства корневой файловой системы в /proc/sys/kernel/real-root-dev. Для смены корневой файловой системы NFS из /linuxrc нужно записать настройки NFS в файлы /proc/sys/kernel/nfs-root-name и /proc/sys/kernel/nfs-root-addrs, а затем записать 0xff (номер псевдо-NFS-устройства) в файл /proc/sys/kernel/real-root-dev. Так, например, следующие команды изменят нормальное корневое устройство на /dev/hdb1:
В качестве примера c NFS, следующие команды изменят нормальное корневое устройство на каталог NFS /var/nfsroot на NFS-сервере локальной сети с IP-адресом 193.8.232.7 для системы с IP-адресом 193.8.232.2 и именем ‘idefix’:
Замечание: Файл /proc/sys/kernel/real-root-dev для смены корневой файловой системы больше не применяется. Современный метод смены корневой файловой системы описан в файле Documentation/initrd.txt из исходного кода ядра Linux, а также в pivot_root(2) и pivot_root(8).
Использование
Возможный сценарий установки системы:
1. Программа-загрузчик стартует с дискеты или другого носителя с минимальным ядром (например, включающим поддержку /dev/ram, /dev/initrd и файловой системы ext2) и загружает в /dev/initrd начальную файловую систему, сжатую программой gzip. 2. Исполняемый файл /linuxrc определяет: (1) что необходимо, чтобы смонтировать нормальную корневую файловую систему (т.е., тип устройства, драйверы, файловую систему) и (2) носитель распространения (например, CD-ROM, сеть, лента, …). Для этого может быть задан вопрос пользователю, запущена автоматического определения или использован гибридный подход. 3. Исполняемый файл /linuxrc загружает необходимые модули из начальной корневой файловой системы. 4. Исполняемый файл /linuxrc создаёт и заполняет корневую файловую систему (на этой стадии нормальная корневая файловая система ещё не является законченной системой). 5. Исполняемый файл /linuxrc устанавливает /proc/sys/kernel/real-root-dev, размонтирует /proc, нормальную корневую файловую систему и все другие файловые системы, которые он монтировал, а затем завершает работу. 6. Затем ядро монтирует нормальную корневую файловую систему. 7. Теперь, поскольку файловая система доступна и полноценна, может быть установлен системный загрузчик. 8. Системный загрузчик настраивается так, чтобы загружать в /dev/initrd файловую систему с набором модулей, которые были использованы для запуска системы в первый раз (например, устройство /dev/ram0 может быть изменено, затем размонтировано и, наконец, образ записывается из /dev/ram0 в файл). 9. Система теперь может загружаться, и можно выполнять дополнительные действия по установке.
Третий вариант — более удобные диски восстановления системы. Поскольку информация, например расположение раздела корневой файловой системы, не нужна во время начальной загрузки, загруженная с /dev/initrd система может использовать диалоговый режим и/или автоматическое определение с последующей, возможно, проверкой правильности.
И наконец, дистрибутивы Linux на CD-ROM могут использовать initrd для упрощения установки с CD-ROM. Дистрибутив может использовать LOADLIN для непосредственной загрузки /dev/initrd с CD-ROM без необходимости в дискетах. Дистрибутив также может использовать загрузочную дискету LILO и затем запускаться с помощью /dev/initrd с большего RAM-диска, находящегося на CD-ROM.
Сказание о Линуксе: загрузчик, initrd и Sys V
Глава 1. Начало: загрузчик.
Глава 2. Initrd.
Эксперимент 1: Как легче всего использовать некоторые из дистрибутивов на LiveCD. Давайте возьмём, к примеру, SLAX 6.0 и сделаем следующее:
Глава 3. Уровни загрузки системы (runlevels).
| Уровень | Выполняется. |
| S | . в процессе загрузки системы. Теоретически, результатом этого уровня должна явиться базовая система с одним пользователем. |
| 0 | . в процессе выключения. |
| 1 | . режим одного пользователя. Используется для администрирования системы с правами root’а. Запущен минимальный набор приложений. Нет сетевых подключений, отсутствует графический интерфейс. |
| 2 | . многопользовательская среда, тестовый режим, подключение либо отсутствует либо обладает ограниченным набором функций. |
| 3 | . полностью многопользовательский режим. Все еще в текстовом режиме, но с поддержкой сети, звука и разных прочих приятностей. |
| 4 | . личные настройки. Обычно то же самое, что в режиме 3. |
| 5 | . то же, что в режиме 3, но с графической оболочкой. Так называемая «полная фукнциональность». |
| 6 | . во время перезагрузки ситемы. |
Таблица 1. Уровни загрузки ОС.
Slackware, уровень загрузки 3
Как я уже писал прежде, «так бывает», и именно так это выглядит, например, в Red Hat или Fedora, но нетрудно понять, что из этого «правила» существует целая кипа исключений. Например:
Slackware, уровень загрузки S
А теперь отбросим прочь монотонность и занудство. Но на самом деле я бы хотел, чтобы всё было гораздо более занудно и монотонно. Текущий уровень загрузки системы можно выяснить, набрав в консоли:
если вы вошли как root. А если как простой смертный, то:
И мы возвращаемся к вопросу о загрузке системы.
Глава 4. Init.
Первой исполняемой программой в готовом к работе окружении является обычно программа init. Эта программа расположена в директории /sbin, ее файлом конфигурации является /etc/inittab. В Linux программа init является ключевой. Почему? Это «родоначальник всех процессов» (выписано из руководства). Более подробно об этом процессе мы расскажем во второй части «Сказания». Сейчас же достаточно будет написать, что в файле конфигурации этой программы есть крайне важная для загрузки строчка. Ее пример (из Debian):
Указанный файл будет исполнен в процессе загрузки. Этот файл устанавливает, какие приложения будут работать во время загрузки, то есть на уровне S. Это довольно важно, потому что впоследствии уровни могут меняться. Скажем, root запускает команду init 1 (в командной строке), чтобы придать себе функции администратора. Он исполняет функции администратора, а затем выполняет init 5, и тут в дело вновь вступаем мы, простые пользователи. Во время переключения уровней загрузки исполняются определенные сценарии, которые должным образом настраивают окружение. Но существуют программы, запускать которые требуется всего лишь один раз. Например, swap partition выполняется при загрузке компьютера и работает дальше, несмотря на то, какой уровень загрузки мы установили, разве не так? Было бы абсолютно неразумно подключать и отключать разделы каждый раз, когда мы меняем уровень загрузки. В этом просто нет смысла. И вновь возвращаясь к программе init: /etc/inittab содержит также еще одну интересную строку:
Это уровень загрузки системы по умолчанию. Если для Вашей, к примеру, Slackware установлен третий уровень, мы можем, изменяя эту строку, установить по умолчанию «графическую» загрузку. Но, конечно же, Вы должны знать, что в Slackware многопользовательским уровнем с графической оболочкой является 4-й, а не, например, 5-й, как в Red Hat. Строка, приведённая в качестве примера, была взята из Debian, а в Debian 2 означает «полный набор функций». На самом деле, /etc/inittab содержит только интересные строки: что система должна делать после нажатия ctrl+alt+del, после перебоя в питании, сколько может быть доступно виртуальных консолей.
Глава 5. Необычайно короткая глава о том, какие программы запускаются.
Программы, обеспечивающие функционирование системы обычно запускаются не «ни с того ни с сего», а благодаря соответствующим сценариям. Эти сценарии расположены по большей части в директории etc/init.d. вот их примеры:
Глава 6. В стиле BSD.
Какой способ выполнения сценариев приложений, требующихся во время работы на определенном уровне загрузки системы является наиболее простым? Это легко: Вы должны написать соответствующий скрипт. Такой метод применяется в системах семейства BSD и в некоторых дистрибутивах Linux, таких, как, например, Slackware или CRUX. В директории /etc/rc. d есть сценарии rc.S, rc.0, rc.4, rc.6 и т.д. Среди этих скриптов есть изящные сценарии, выполняющие запуск соответствующих программ, и даже сценарии автономных приложений. Что значит здесь слово «изящные»? Ну вот вам пример:
Это значит примерно следующее: «Если есть какой-либо графический GNU-менеджер входа в систему, запусти его». «Изящный» применяется по отношению к управлению или информации, если то, чего мы ждем от системы, вообще возможно. На самом деле, здесь действует простое правило: мы пишем сценарий, который ведет машину к начальному уровню загрузки («S»), а затем и к желаемому: например, 4-му, который в случае Slackware означает полный набор функций. Подобным же образом мы создаем скрипт для запуска приложений, которые предоставляют возможности уровню root, скрипт, для перезапуска системы и т.п. Можно ли сделать это по-другому?
Глава 7. В стиле SysV
Эксперимент 2. Давайте создадим на нашем компьютере директорию. Наш эксперимент может быть успешно проведен и в Slackware: мы будем использовать стандартные команды. Давайте назовем нашу директорию «scripts».
Отлично. Теперь давайте создадим в этой директории три файла, как описано ниже:
Мы создадим остальные два файла таким же образом, только все последовательности символов «01» заменим на «02» и «03». Конечно же, Вы можете создать хоть сотню таких, если захотите. Для того, чтобы «вспомнить» последнюю команду и изменить ее, я предлагаю использовать клавишу «стрелка вверх». Следующим шагом будет придание нашим псевдосценариям свойства исполняемости:
А теперь самое интересное. Как нам уже известно, мы можем выполнить любой сценарий, например:
Вы можете также выполнить сразу несколько, например:
Пока что ничего нового. Но что, если мы хотим выполнить все сценарии, а их там не 5, а 105? Ну, или что, если другой пользователь нашего ПК, Боб Кодер, добавил еще два сценария? (Что, конечно, отнюдь не значит, что он плохой программист. Или все-таки плохой?) Так что же делать? Выписывать имена всех файлов, потом напряженно «вбивать» их в командную строку? Конечно же, нет. Система содержит определенную команду, которой мы прямо сейчас и воспользуемся:
И теперь о самом графическом менеджере входа в систему. Благодаря наличию номеров в именах сценариев Вы можете достичь эффекта, сходного с тем, что мы наблюдаем в MS Windows©, когда система на самом деле еще загружает требуемые приложения, но пользователь уже видит экран входа. Вам просто нужно передвинуть запуск программы gdm на несколько более раннее время. Но есть ли в этом какой-либо смысл? Решение этого вопроса я оставляю на суд читателя. Сценарий также может быть выполнен при помощи следующей команды:
Что значит: для каждого файла, расположенного в директории /etc/rcS.d/ и обладающего именем, подходящим под шаблон «S, плюс два символа, плюс еще что угодно», выполнить. Debian использует этот метод наравне с программой «запуска-по-частям». Будет ли используемый в Debian способ запуска приложений для нужд уровня загрузки системы лучше того, который используется в Slackware? Это непростой вопрос. Как я уже упоминал ранее, в Slackware Вам нужно напряженно печатать в сто втором, сто третьем, сто четвертом скрипте из тех, что Вам нужны, а в Debian все это как будто проще, но с другой стороны. Когда Вы редактируете файлы, которыми хотите воспользоваться, Вы теоретически больше осведомлены о том, что случится с вашей системой, а в сценариях Slackware вы можете писать собственные комментарии. Однако для Debian и его «семьи» существуют специальные программы: update-rc.d (запускается в консоли) или KsysV (это часть среды KDE), которые делают проще управление символьными ссылками на сценарии для определенных уровней загрузки системы.
Конечно, можно применять различные методы.
Глава 8. GoboLinux.
Подробнее узнать о процессе загрузки вы можете в статье «Исследуем процесс загрузки Linux» и других статьях раздела «Настройка процедур загрузки системы» нашей Библиотеки.
initrd — Initial RAM disk
Начальный RAM диск для загрузки Linux (initrd) это временная корневая файловая система, которая монтируется в процессе загрузки системы в оперативную память для поддержки 2х уровневой модели загрузки. Initrd состоит из различных исполняемых файлов и драйверов, которые позволяют смонтировать настоящую корневую файловую систему, после чего initrd
размонтируется и освобождается память. Во многих встраиваемых системах initrd так и остаётся корневой файловой системой. В этой статье исследуется загрузочный RAM диск для ядра Linux 2.6, включая процесс его создания и использования в ядре линукса.
Что такое загрузочный RAM диск?
Загрузочный RAM диск (Initrd) это образ корневой файловой системы, который монтируется до того как настоящая корневая фс будет доступна. Initrd связан с ядром и загружается как часть ядра в процессе загрузки системы. Ядро монтирует образ initrd в котором находятся необходимые модули для монтирования корневой фс и уже дальнейшего перехода в этот корень как основной.
В initrd содержится минимальный набор директорий и исполняемых файлов для загрузки модулей, например insmod для загрузки модулей ядра.
В случае настольной системы или сервера, initrd временная файловая система. Её время жизни коротко и служит лишь связующим звеном к корневой фс. Во встраиваемых системах нет записываемых устройств хранения данных, поэтому initrd является постоянной корневой фс. В этой статье исследуются оба варианта.
Внутреннее устройство initrd
В образе initrd содержится необходимый минимум исполняемых и системных файлов для осуществления второй стадии загрузки linux. В зависимости от версии linux которую вы используете, различаются методы создания initrd.
Начиная с Fedora Core 3, по умолчанию образ initrd это сжатый cpio архив. Вместо монтирования файла с использованием loop device, нужно использовать программу cpio. Чтобы исследовать содержимое cpio архива, используйте следующую последовательность команд:
Листинг 2. Исследование initrd (FC3 и более поздние версии)
Интерес в листинге 3 представляет файл init в корне. Это файл, как и в традиционном процессе загрузки linux, запускается когда образ initrd распаковывается в память. Мы разберем этот процесс позже в этой статье.
Утилиты для создания образа initrd
Давайте вернемся в начало и формально разбремся как создается образ initrd. Для традиционных linux систем образ initrd содается в процессе установки. Огромное количество программ, таких как mkinitrd, могут быть использованны для создания initrd с необходимыми библиотеками и модулями для связи с реальной корневой фс. Mkinitrd это по сути обычный shell
скрипт, так что вы можете посмотреть каким образом достигается нужный нам результат. Есть так же YAIRD (Yet Another Mkinitrd) — программа, которая позволяет настроить практически любой параметр в initrd.
Команда cpio Используя команду cpio, вы можете манипулировать cpio файлами. Файл Cpio это по сути простая конкатенция файлов с заголовками. Формат файла cpio позволяет работать как с ascii файлами, так и с бинарными. Для совместимости используйте ascii, для уменьшения размера — бинарную версию.
Создание вручную индивидуального образа initrd
По причине того что на многих встраиваемых системах, основанных на linux нет жесткого диска, initrd так же является и постоянной фс. Листинг 4 показывает как создавать образ initrd. Я использую стандартный десктоп с linux’ом, так что вы можете опробовать эти действия не имея под рукой встраиваемой системы с linux’ом. Не считая компиляции под другую
платформу, концепция создания образа initrd одинакова для встраиваемых систем и обычных компьютеров (включая сервера).
Листинг 4. Утилита (mkird) для создания индивидуального образа initrd
# Ramdisk Constants
RDSIZE=4000
BLKSIZE=1024
# Create an empty ramdisk image
dd if=/dev/zero of=/tmp/ramdisk.img bs=$BLKSIZE count=$RDSIZE
# Populate the filesystem (subdirectories)
mkdir /mnt/initrd/bin
mkdir /mnt/initrd/sys
mkdir /mnt/initrd/dev
mkdir /mnt/initrd/proc
# Create the init file
cat >> /mnt/initrd/linuxrc kernel /bzImage-2.6.1
[Linux-bzImage, setup=0x1400, size=0x29672e]
grub> initrd /ramdisk.img.gz
[Linux-initrd @ 0x5f2a000, 0xb5108 bytes]
Uncompressing Linux. OK, booting the kernel.
После того как ядро запущенно, оно проверяет доступен ли initrd
(подробнее об этом позже) и образ initrd доступе, ядро загружает его в
память и монтирует как корневую фс. Вы можете посмотреть на процесс
загрузки в листинге 6. Когда initrd загружен, становится доступна
оболочка ash. В этом примере я исследую корневую фс и опрашиваю
виртуальную файловую систему /proc. Я так же демонстрирую возможность
писать на файловую создавая файл командой touch. Обратите внимание на то
что первый созданный процесс это linuxrc (обычно init).
Листинг 6. Загрузка ядра linux с простым initrd
Процесс загрузки с начальным RAM диском
Теперь вы знаете как собрать свой образ initrd, в этой часте астатьи мы
рассмотрим как ядро идентифицирует и монтируется initrd как корневую фс.
Я прошелся через этот процесс останавливаясь на некоторых важных
функциях в цепочке загрузки, объясняя что происходит.
Загрузчик, например GRUB, идентифицирует ядро, которое нужно загрузить и
копирует образ ядра и initrd в память. Вы можете найти описание всех
необходимых функций в каталоге /init в дереве исходных кодов ядра.
После того как ядро и initrd распакованы и скопированны в память,
исполняется ядро. В этом месте происходит много разных инициализационных
процедур, в конечном счете вы оказываетесь в функции init/main.c:init()
(поддиректория/файл:функция). Эта функция как раз и представляет собой
подсистему инициализации. Отсюда идет вызов функции
init/do_mounts.c:prepare_namespaces(), которая подготавливает рабочее
пространство (монтирует файловую систему dev, RAID или md устройства и,
в итоге, сам initrd). Загрузка initrd выполняется вызовом
init/do_mounts_initrd.c:initrd_load().
Функция initrd_load() вызывает init/do_mounts_rd.c:rd_load_image()
которая определяет браз RAM диска, чтобы загрузить его в
init/do_mounts_rd.c:identify_ramdisk_image(). Это функция проверяет
«магическое число» образа чтобы определить какая фс в этом образе:
minux, ext2, romfs,cramfs или формат gzip. Возвраящаясь к функции
initrd_load_image, происходит вызов init/do_mounts_rd:crd_load(). Эта
функция выделяет в памяти место под RAM диск и считает контрольную сумму
(CRC), после этого распаковывает и загружает RAM диск в память. Теперь
ваш образ initrd находится в подходящем для монтирования блочном
устройстве.
Монтирование блочного устройства в качестве корневого раздела начинается
в функции init/do_mounts.c:mount_root(). Создаётся корневой раздел и
происходит вызов init/do_mounts.c:mount_block_root(). Отсюда вызывается
init/do_mounts.c:do_mount_root(), который вызывает
fs/namespace.c:sys_mount(), чтобы уже смонтировать корневую фс и перейти
в неё. Это то место где выводится в консоль знакомое нам по листингу 6
сообщение VFS: Mounted root (ext2 file system).
В конце, вас возвращает в функцию init и происходит вызов
init/main.c:run_init_process. Это происходит в результате вызова execve
чтобы запустился процесс init (в данном случае linuxrc). Linuxrc может
быть как исполняемым файлом, так и скриптом (если для скрипта есть
интерпретатор).
В листинге 7 показана иерархия функций. Не все функции учавствующие в
процессе копирования и монтирования инициализационного RAM диска
показаны здесь, но общее представление об общем процессе здесь дано.
Листинг 7. Иерархия основных функций участвующих в процессе загрузки и
монтирования initrd
init/main.c:init
init/do_mounts.c:prepare_namespace
init/do_mounts_initrd.c:initrd_load
init/do_mounts_rd.c:rd_load_image
init/do_mounts_rd.c:identify_ramdisk_image
init/do_mounts_rd.c:crd_load
lib/inflate.c:gunzip
init/do_mounts.c:mount_root
init/do_mounts.c:mount_block_root
init/do_mounts.c:do_mount_root
fs/namespace.c:sys_mount
init/main.c:run_init_process
execve
Загрузка бездисковых систем
Почти как во встраиваемых системах, локальный диск (флоппи или CD-ROM)
не обязательны чтобы загрузить ядро и корневую фс в RAM. Протокол
динамического назначения адресов или просто DHCP может быть использован
для того чтобы определить такие параметры как IP адресс и маску подсети.
Упрощенный протокол передачи данных (TFTP) может быть использован для
передачи образа ядра и образа initrd на локальную машину. После
передачи, ядро может быть загружено, а initrd смонтирован, так как будто
это загрузка с локальной машины.
Уменьшение размера образа initrd
В этом примере BusyBox скомпилирован статически, т.е. ему не нужно для
работы никаких библиотек. Однако, еесли вам нужны стандартные библиотеки
языка C (для дополнительных программ), есть нескольно путей решить эту
проблемму не используя массивный glibc. Первая маленькая библиотека
подходящая для наших целей это uClibc, которая является минимизированной
версией стандартной библиотеки C для систем с ограниченным местом.
Другая библиотека, идеальная с точки зрения занимаемого дискового
пространства это dietlib. Не забывайте что для того чтобы ваши программы
работали с урезанными версиями, эти программы нужно перекомпилировать
используя эти библиотеки, потребуется некоторая дополнительная работа,
но оно того стоит.
Анализ процесса загрузки ядра Linux
Пока Леонид готовится к своему первому открытому уроку по нашему курсу «Администратор Linux», мы продолжаем рассказывать про загрузку ядра Linux-а.
Понимание работы системы, функционирующей без сбоев — подготовка к устранению неизбежных поломок
Древнейшая шутка в области ПО с открытым исходным кодом — заявление, что “код документирует сам себя”. Опыт показывает, что чтение исходного кода похоже на прослушивание прогнозов погоды: разумные люди все равно выйдут на улицу и посмотрят на небо. Ниже приводятся советы для проверки и изучения загрузки систем Linux с помощью знакомых инструментов отладки. Анализ процесса загрузки системы, которая работает хорошо, готовит пользователей и разработчиков к устранению неизбежных сбоев.
С одной стороны, процесс загрузки на удивление прост. Ядро операционной системы (kernel) запускается однопоточно и синхронно на одном ядре (core), что может показаться понятным даже жалкому человеческому уму. Но как запускается само ядро ОС? Какие функции выполняют initrd (диск в оперативной памяти для начальной инициализации) и загрузчики? И постойте, почему всегда горит светодиод в Ethernet-порте?
Читайте дальше, чтобы получить ответы на эти и некоторые другие вопросы; код описанных демо и упражнений также доступен на GitHub.
Начало загрузки: состояние OFF
Состояние OFF означает, что у системы нет питания, верно? Кажущаяся простота обманчива. Например, светодиод Ethernet горит даже в этом состоянии, потому что в вашей системе включен wake-on-LAN (WOL, пробуждение по [сигналу из] локальной сети). Убедитесь, написав:
Где вместо может быть, например, eth0 (ethtool находится в пакетах Linux с тем же названием). Если «Wake-on» в выводе показывает g, удаленные хосты могут загрузить систему, отправив MagicPacket. Если вы не хотите удаленно включать свою систему сами и давать такую возможность другим, отключите WOL в системном BIOS меню, или с помощью:
Процессор, отвечающий на MagicPacket, может быть Baseboard Management Controller’ом (BMC) или частью сетевого интерфейса.
Intel Management Engine, Platform Controller Hub и Minix
BMC — не единственный микроконтроллер (MCU), который может “слушать” номинально выключенную систему. В системах x86_64 есть программный пакет Intel Management Engine (IME) для удаленного управления системами. Широкий спектр устройств, от серверов до ноутбуков, обладают технологией, которая обладает таким функционалом, как KVM Remote Control или Intel Capability Licensing Service. Согласно собственному инструменту Intel, в IME есть непропатченные уязвимости. Плохие новости — отключить IME сложно. Траммелл Хадсон (Trammell Hudson) создал проект me_cleaner, стирающий некоторые наиболее вопиющие компоненты IME, например, встроенный веб-сервер, но в то же время есть шанс, что использование проекта превратит систему, на которой он запущен, в кирпич.
Прошивка IME и программа System Management Mode (SMM), которая следует за ней на загрузке, основаны на операционной системе Minix и запускаются на отдельном процессоре Platform Controller Hub, а не основном ЦП системы. Затем SMM запускает на основном процессоре программу Universal Extensible Firmware Interface (UEFI), о которой писали уже не раз. Группа Coreboot начала в Google захватывающе амбициозный проект Non-Extensible Reduced Firmware (NERF), цель которого — заменить не только UEFI, но и ранние компоненты пользовательского пространства Linux, например, systemd. А пока мы ждем результатов, пользователи Linux могут приобрести ноутбуки от Purism, System76 или Dell, на которых IME отключен, плюс, можем надеяться на ноутбуки с 64-битным ARM процессором.
Загрузчики
Что помимо запуска забагованного шпионского ПО делает загрузочная прошивка? Задача загрузчика — предоставить только что включенному процессору необходимые ресурсы для запуска операционной системы общего назначения вроде Linux. Во время включения нет не только виртуальной памяти, но и DRAM до момента поднятия ее контроллера. Затем загрузчик включает источники питания и сканирует шины и интерфейсы, чтобы найти образ ядра и корневую файловую систему. Популярные загрузчики, например, U-Boot и GRUB, обладают поддержкой как распространенных интерфейсов вроде USB, PCI и NFS, так и других более специализированных встроенных устройств, таких как NOR- и NAND-flash. Загрузчики также взаимодействуют с аппаратными устройствами безопасности, например, Trusted Platform Module (TPM), чтобы установить цепочку доверия с начала загрузки.
Запуск загрузчика U-boot в песочнице на сервере сборки.
Популярный загрузчик с открытым исходным кодом U-Boot поддерживается системами от Raspberry Pi до устройств Nintendo, автомобильных плат и Chromebook’ов. Системный журнал отсутствует, а если что-то идет не так, может не быть даже консольного вывода. Чтобы облегчить отладку, команда U-Boot предоставляет песочницу для тестирования патчей на хосте сборки или даже в системе Непрерывной Интеграции. На системе, где установлены обычные инструменты для разработки вроде Git и GNU Compiler Collection (GCC), разобраться в песочнице U-Boot не составит труда.
Вот и все: вы запустили U-Boot на x86_64 и можете тестировать каверзные фичи, например, переразбиение фиктивных запоминающих устройств, манипуляцию секретными ключами на базе TPM и горячее подключение (hotplug) USB-устройств. Песочница U-Boot может быть одноэтапной в рамках отладчика GDB. Разработка с использованием песочницы в 10 раз быстрее, чем тестирование путем перезаписи загрузчика на плату, плюс, “кирпичную” песочницу можно восстановить нажатием Ctrl+C.
Снабжение загружающегося ядра
По завершении своих задач, загрузчик выполнит переключение на код ядра, которое он загрузил в основную память, и начнет его исполнение, передав все параметры командной строки, которые уточнил пользователь. Какой программой является ядро? file /boot/vmlinuz показывает, что это bzImage. В дереве источников Linux есть инструмент extract-vmlinux, который можно использовать для распаковки файла:
Ядро представляет собой Executable and Linking Format (ELF) бинарный файл, как и программы пользовательского пространства Linux. Это значит, что мы можем использовать команды из пакета binutils, такие как readelf, чтобы его изучить. Сравните, например, такие выводы:
Список разделов в бинарных файлах по большей части аналогичен.
От start_kernel() до PID 1
Манифест оборудования ядра: таблицы ACPI и деревья устройств
При загрузке ядру необходима информация об оборудовании помимо типа процессора, для которого оно было скомпилировано. Инструкции в коде дополнены конфигурационными данными, которые хранятся отдельно. Существует два основных метода хранения данных: деревья устройств (Device Tree) and ACPI таблицы. Ядро узнает из этих файлов, какое оборудование нужно запускать на каждой загрузке.
ACPI таблицы на ноутбуках Lenovo готовы к Windows 2001.
Да, ваша Linux система готова к Windows 2001, если захотите ее установить. В ACPI есть как методы, так и данные, в отличие от ДУ, который больше похож на язык описания аппаратуры. Методы ACPI продолжают быть активными и после загрузки. Например, если запустить команду acpi_listen (из пакета apcid), а затем закрыть и открыть крышку ноутбука, увидим, что функционал ACPI продолжал работать все это время. Временная и динамическая перезапись таблиц ACPI возможна, но для постоянного изменения потребуется взаимодействие с меню BIOS на загрузке или перепрошивка ПЗУ. Вместо таких сложностей, возможно вам стоит просто установить coreboot, замену прошивки с открытым исходным кодом.
От start_kernel() до пользовательского пространства
где поле PSR означает “процессор”. Каждое ядро (core) должно иметь собственные таймеры и cpuhp обработчики hotplug.
Раннее пользовательское пространство: кто заказывал initrd?
Веселье с rescue shell и кастомным initrd.
initrd также отлично подходит для тестирования файловых систем и устройств хранения данных. Положите инструменты для тестирования в initrd и запустите тесты из памяти, а не из тестируемого объекта.
Процесс загрузки Linux звучит запретно, с учетом количества затронутого ПО, даже на простейшем встроенном устройстве. С другой стороны, процесс загрузки довольно прост, так как излишняя сложность, вызванная вытесняющей многозадачностью, RCU и состоянием гонки, здесь отсутствует. Обращая внимание только на ядро и PID 1, можно упустить из виду большую работу, проделанную загрузчиками и вспомогательными процессорами для подготовки платформы к запуску ядра. Ядро, безусловно, отличается от других программ Linux, но применение инструментов для работы с другими бинарными файлами ELF поможет лучше понять его структуру. Изучение работоспособного процесса загрузки подготовит к сбоям, возможным в будущем.
Ждём ваши комментарии и вопрос, как обычно, или тут, или на нашем открытом уроке, где отдуваться будет Леонид.
