Насколько маленьким может быть ядро linux?

?v=1
Некоторое время назад я научился конвертировать виртуальные машины в oracle cloud из ubuntu 20.04 в gentoo. Машины предоставляемые в рамках always free tier весьма маломощны. Это в частности приводит к тому, что перекомпиляция ядра превращается в достаточно длительный процесс. У исходного ядра ubuntu 20.04 в конфиге было 7904 параметра. После того, как я сделал
make localmodconfig && make localyesconfig

число параметров уменьшилось до 1285. Мне стало интересно попробовать выбросить из ядра все лишнее и посмотреть, что получится.
Я буду компилировать ванильное ядро 5.4.0, потому что именно эта версия используется на моей установке gentoo. Для ускорения процесса я компилирую ядро на своей рабочей машине (i7, 8 cores, 64Gb RAM, tmpfs). Готовое ядро я копирую на машину в oracle cloud. Начинать процесс надо с команды
make tinyconfig

В результате у вас появится файл .config для самого минимального ядра текущей архитектуры. В моем случае в этом файле оказалось 284 непустых строк, не являющихся комментариями. Давайте скомпилируем его и посмотрим на размер ядра:
$ yes ""|make -j$(nproc)
$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 441872 Jan 31 18:09 arch/x86/boot/bzImage
284
$
Это ядро абсолютно бесполезно. Оно не только не может загрузиться, но даже не имеет возможности сообщить о возникших проблемах. Давайте это исправим. Для активации параметров я буду использовать команду
script/config -e config_parameter_name

Итак, наше ядро будет 64-х битным, мы активируем вывод диагностики ядра, включаем поддержку терминала, конфигурируем последовательный порт и консоль на нем:
./scripts/config -e CONFIG_64BIT -e CONFIG_PRINTK -e CONFIG_TTY -e CONFIG_SERIAL_8250 -e CONFIG_SERIAL_8250_CONSOLE

Машина в oracle cloud загрузиться с этим ядром не сможет, вывода на консоль не будет. Как оказалось, надо добавить поддержку EFI и ACPI, являющуюся ее зависимостью. Скрипт ./scripts/config не реализует логику добавления обратных зависимостей т.е. если добавить только CONFIG_EFI, то make выкинет этот параметр из конфига. Также стоит отметить, что включение опций часто включает нижележащие опции. Так в случае с включением CONFIG_ACPI автоматически включается, к примеру, поддержка кнопки включения/выключения.
./scripts/config -e CONFIG_ACPI -e CONFIG_EFI -e CONFIG_EFI_STUB

Собираем ядро
$ yes ""|make -j$(nproc)
$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 1036960 Jan 31 18:13 arch/x86/boot/bzImage
409
$

Это ядро по прежнему не может завершить процесс загрузки, но по крайней мере способно сообщить об этом:
Kernel panic - not syncing: No working init found.  Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance.
Kernel Offset: 0x22000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
---[ end Kernel panic - not syncing: No working init found.  Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance. ]---

Давайте добавим необходимые параметры:
# virtual guest support and pci
./scripts/config -e CONFIG_PCI -e CONFIG_VIRTIO_PCI -e CONFIG_VIRTIO -e CONFIG_VIRTIO_MENU -e CONFIG_PARAVIRT -e CONFIG_HYPERVISOR_GUEST  
# disk support
./scripts/config -e CONFIG_BLOCK -e CONFIG_SCSI -e CONFIG_BLK_DEV_SD -e CONFIG_SCSI_VIRTIO
# filesystems
./scripts/config -e CONFIG_EXT4_FS -e CONFIG_PROC_FS -e CONFIG_SYSFS -e CONFIG_DEVTMPFS -e CONFIG_DEVTMPFS_MOUNT
# executable formats
./scripts/config -e CONFIG_BINFMT_ELF -e CONFIG_BINFMT_SCRIPT
# network
./scripts/config -e CONFIG_NET -e CONFIG_VIRTIO_NET -e CONFIG_PACKET -e CONFIG_UNIX -e CONFIG_INET -e CONFIG_NET_CORE -e CONFIG_NETDEVICES -e CONFIG_VIRTIO_NET

Собираем ядро:
$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 1950368 Jan 31 18:18 arch/x86/boot/bzImage
616
$

И загружаемся. На этот раз ядро успешно находит корневой диск, но в консоли мы видим ошибки:
The futex facility returned an unexpected error code.
...
 * Call to flock failed: Function not implemented

Пытаемся залогиниться:
(none) login: root
process 182 (login) attempted a POSIX timer syscall while CONFIG_POSIX_TIMERS is not set
Password:
setgid: Function not implemented

Тоже не получается, но зато нам недвусмысленно подсказывают какой параметр надо добавить. Добавляем его и остальные необходимые параметры:
./scripts/config -e CONFIG_POSIX_TIMERS -e CONFIG_FUTEX -e CONFIG_FILE_LOCKING -e CONFIG_MULTIUSER

Собираем ядро:
$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 1979040 Jan 31 18:25 arch/x86/boot/bzImage
623
$

На этот раз нам удается залогиниться:
instance-20210124-1735 login: root
Password:
Last login: Mon Feb  1 02:25:10 UTC 2021 from 73.239.106.74 on ssh
root@instance-20210124-1735:~#

Ура! Ssh тоже работает. Тем не менее в консоли опять есть ошибки:
 * Some local filesystem failed to mount
...
hwclock: Cannot access the Hardware Clock via any known method.
hwclock: Use the --verbose option to see the details of our search for an access method.
 * Failed to set the system clock

А в dmesg находим еще:
[    2.910198] udevd[360]: inotify_init failed: Function not implemented

Добавляем поддержку часов реального времени, файловой системы vfat и inotify:
./scripts/config -e CONFIG_RTC_CLASS -e CONFIG_DNOTIFY -e CONFIG_INOTIFY_USER -e CONFIG_VFAT_FS

Собираем ядро:
$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 2015904 Jan 31 18:36 arch/x86/boot/bzImage
643
$

Хммм, vfat диск по прежнему не может подмонтироваться:
 * Some local filesystem failed to mount

А вот и ответ почему в dmesg заодно с еще одной ошибкой:
[    3.782884] udevd[527]: error creating signalfd
[    4.107698] FAT-fs (sda15): codepage cp437 not found

Добавляем параметры:
./scripts/config -e CONFIG_SIGNALFD -e CONFIG_NLS_CODEPAGE_437 -e CONFIG_NLS_ISO8859_1

Собираем ядро:
$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 2015904 Jan 31 18:41 arch/x86/boot/bzImage
646
$

Загружаемся, в консоли ошибок нет, но появилась новая ошибка в dmesg:
[    2.756136] udevd[360]: error creating epoll fd: Function not implemented

Исправляем:
./scripts/config -e CONFIG_EPOLL

Собираем ядро:
$ ll arch/x86/boot/bzImage && grep -v ^# .config|grep -c .
-rw-rw-r-- 1 kvt kvt 2020000 Jan 31 19:13 arch/x86/boot/bzImage
647
$

Перезагружаемся и на этот раз не видим новых ошибок:)!

На моей рабочей машине (i7, 8 cores, 64gb RAM, tmpfs) финальная конфигурация собирается за 1m 16s. В oracle cloud с двумя ядрами и на обычном диске этот же процесс занимает 19m 51s.

Получившееся ядро не является абсолютно минимальным. Так, например, включение поддержки сети добавляет кучу разных сетевых адаптеров. Я не стал заниматься перфекционизмом и вычищать абсолютно все, что не нужно. Кроме того хочу предупредить, что хотя загрузка и быстрое тестирование не выявило проблем с отсутствием дополнительных важных компонент ядра скорее всего таковые существуют. Так что если вдруг вы решите переиспользовать мой конфиг пожалуйста тщательно протестируйте ядро для вашего конкретного случая и при необходимости добавьте нужные параметры ядра.

© Habrahabr.ru