Библиотека — Linux — ядро: сборка, модули, параметры [X]
Шифр
linux-kernel
Разделы
linux tooling
Сверено
2 сентября — в этот день утверждения статьи сверялись с первоисточниками, а не правился текст
Объём
21 326 знаков · 22 раздела · чтения около 20 минут

Linux — ядро: сборка, модули, параметры


Обзор

Linux — монолитное ядро с поддержкой загружаемых модулей. Это компромисс: ядро работает в едином адресном пространстве (быстрые вызовы, нет IPC-overhead как в микроядрах), но модули позволяют расширять функциональность без перекомпиляции и перезагрузки. На практике большинство пользователей никогда не собирают ядро сами — дистрибутивы поставляют generic-ядра с сотнями модулей. Но понимание архитектуры сборки, модульной системы и runtime-параметров необходимо для отладки, performance-тюнинга и работы с нестандартным оборудованием.

Ядро Linux развивается в модели rolling release: новый релиз каждые 9-10 недель, LTS-ветки поддерживаются от трёх до шести лет. Нумерация не отражает масштаба изменений — за 6.19 (февраль 2026) последовала 7.0 (апрель 2026), это обычная смена счётчика, а не смена поколения. Действующие LTS-ветки — 6.18, 6.12, 6.6 и 6.1, плюс 5.15 и 5.10, у которых EOL намечен на декабрь 2026. Дистрибутивы обычно берут LTS-ядро и бэкпортят фиксы, создавая свою ветку (например, Debian 13 использует 6.12, RHEL 9 — 5.14 с массивным бэкпортингом).

Архитектура: Kconfig / Kbuild

Конфигурация: Kconfig

Ядро содержит ~18000+ конфигурационных символов (CONFIG_*), определённых в файлах Kconfig по всему дереву исходников. Каждый символ имеет тип (bool, tristate, int, string), зависимости (depends on), значения по умолчанию, и help-текст. Kconfig — это язык, а make menuconfig / xconfig / nconfig — фронтенды, парсящие Kconfig-файлы и генерирующие .config.

Тип tristate — ключевая абстракция модульности: y (built-in, линкуется в vmlinuz), m (module, компилируется как .ko), n (отключено). Bool — только y/n (не может быть модулем). Зависимости определяют видимость: если CONFIG_NET=n, все сетевые опции скрыты в menuconfig.

# Пример Kconfig-записи (drivers/net/ethernet/intel/Kconfig)
config E1000E
    tristate "Intel(R) PRO/1000 PCI-Express Gigabit Ethernet support"
    depends on PCI
    select CRC32
    help
      This driver supports Intel PRO/1000 PCI-Express Ethernet adapters.

depends on PCI — опция видна только если PCI включён. select CRC32 — при включении E1000E автоматически включается CRC32. select — мощный, но опасный: он форсирует включение зависимости, игнорируя её собственные depends on. Это частый источник ошибок конфигурации в ядре.

Сборка: Kbuild

Kbuild — make-based система сборки. Каждый каталог содержит Makefile с записями вида:

obj-$(CONFIG_E1000E) += e1000e/
obj-y += core.o          # всегда линкуется
obj-m += mydriver.o      # всегда модуль (редко, обычно через CONFIG_*)

Когда CONFIG_E1000E=y, obj-y включает каталог — код линкуется в ядро. Когда =m, obj-m — код компилируется в e1000e.ko. Когда =n, строка превращается в obj-= и игнорируется.

Результат сборки: сжатый образ ядра (на x86 это arch/x86/boot/bzImage, в /boot он попадает под именем vmlinuz-*), modules/*.ko (загружаемые модули), System.map (таблица символов для отладки), .config (сохранённая конфигурация).

С 6.1 в дереве живёт вторая тулчейн-ветка: CONFIG_RUST включает поддержку Rust, и тогда сборке помимо компилятора C нужны rustc, bindgen и libclang. Требования к версиям жёсткие и растут от релиза к релизу (в дереве 7.x — rustc 1.85.0 и bindgen 0.71.1), поэтому Kconfig прячет опцию, пока не выполнено RUST_IS_AVAILABLE; проверить тулчейн вручную можно целью make LLVM=1 rustavailable.

Процесс сборки

# 1. Получить исходники
# Из git:
git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
# Или tarball с kernel.org

# 2. Конфигурация
# Начать с конфига текущего ядра:
cp /boot/config-$(uname -r) .config
make olddefconfig          # применить defaults для новых символов

# Или начать с дефолтов архитектуры:
make defconfig

# Интерактивная настройка:
make menuconfig            # ncurses TUI
make nconfig               # улучшенная ncurses версия
make xconfig               # Qt GUI

# 3. Сборка
make -j$(nproc)            # ядро + модули
make modules_install       # установить модули в /lib/modules/<version>
make install               # установить vmlinuz + System.map + обновить bootloader

# 4. Обновить initramfs (если нужно)
# Debian/Ubuntu:
update-initramfs -c -k <version>
# Fedora/RHEL:
dracut --force /boot/initramfs-<version>.img <version>

Загружаемые модули

Жизненный цикл модуля

Модуль (.ko — kernel object) — ELF-файл с дополнительными секциями метаданных. При загрузке ядро: верифицирует подпись (если CONFIG_MODULE_SIG_FORCE=y), разрешает символы (функции/данные, экспортированные другими модулями или ядром через EXPORT_SYMBOL), выделяет память и загружает код, вызывает init-функцию модуля.

Модули могут устанавливаться сжатыми: CONFIG_MODULE_COMPRESS_ZSTD даёт суффикс .ko.zst, CONFIG_MODULE_COMPRESS_XZ — .ko.xz, CONFIG_MODULE_COMPRESS_GZIP — .ko.gz. Разжимает их kmod, а при CONFIG_MODULE_DECOMPRESS — само ядро. Отсюда маска *.ko* в примерах поиска модулей по дереву: одного *.ko уже недостаточно.

# Загрузка/выгрузка
modprobe e1000e              # загрузить + зависимости (из /lib/modules/...)
modprobe -r e1000e           # выгрузить + неиспользуемые зависимости
insmod ./mydriver.ko         # загрузить по пути (без dependency resolution)
rmmod mydriver               # выгрузить (без dependency resolution)

# Информация
lsmod                        # список загруженных модулей + refcount
modinfo e1000e               # метаданные: version, license, depends, params
modprobe -c                  # вся конфигурация modprobe

# Поиск и зависимости
depmod -a                    # перестроить modules.dep (после установки новых модулей)
modprobe --show-depends e1000e  # показать дерево зависимостей

modprobe vs insmod

insmod — низкоуровневый: загружает один .ko файл по абсолютному пути, не разрешает зависимости. modprobe — высокоуровневый: ищет модуль по имени в /lib/modules/$(uname -r)/, автоматически загружает зависимости из modules.dep, читает конфигурацию из /etc/modprobe.d/. Аналогия: insmod ~ dpkg, modprobe ~ apt.

Конфигурация модулей

/etc/modprobe.d/*.conf — конфигурация загрузки модулей:

# Передача параметров модулю
options snd-hda-intel power_save=1 power_save_controller=Y

# Blacklist — запретить автозагрузку модуля
blacklist nouveau             # отключить nouveau (для установки NVIDIA driver)
install nouveau /bin/false    # жёстче: при попытке загрузить — ничего не делать

# Alias — маппинг имён
alias net-pf-10 ipv6          # при запросе "net-pf-10" загрузить ipv6

Параметры модулей можно также задать через строку загрузки ядра (kernel command line): e1000e.InterruptThrottleRate=3000.

DKMS: Dynamic Kernel Module Support

DKMS решает проблему out-of-tree модулей (NVIDIA, VirtualBox, ZFS): при обновлении ядра модули нужно пересобрать. DKMS автоматизирует это — хранит исходники модуля и пересобирает его для каждого нового ядра при установке. Конфигурация в /usr/src/-/dkms.conf.

Подпись модулей

Secure Boot требует подписи ядра и модулей. При CONFIG_MODULE_SIG_FORCE=y ядро отказывается загружать неподписанные модули. Дистрибутивные ядра подписаны ключом дистрибутива. Для загрузки сторонних модулей (NVIDIA, DKMS) необходимо: либо зарегистрировать свой MOK (Machine Owner Key) через mokutil, либо отключить Secure Boot.

Параметры ядра: sysctl и /proc/sys

Архитектура

sysctl — интерфейс для runtime-модификации параметров ядра через виртуальную файловую систему /proc/sys/ (см. статью 1). Маппинг прямой: net.ipv4.ip_forward ↔ /proc/sys/net/ipv4/ip_forward. Чтение: sysctl net.ipv4.ip_forward или cat /proc/sys/net/ipv4/ip_forward. Запись: sysctl -w net.ipv4.ip_forward=1 или echo 1 > /proc/sys/net/ipv4/ip_forward.

Параметры организованы в подсистемы: kernel.* (общие: panic, hostname, pid_max, shmmax), vm.* (виртуальная память: swappiness, dirty_ratio, overcommit), net.* (сеть — подробно в статье 3), fs.* (файловые системы: file-max, inotify limits), dev.* (устройства).

Постоянная конфигурация

Файлы в /etc/sysctl.d/*.conf загружаются при загрузке системы (systemd-sysctl.service). Порядок: /usr/lib/sysctl.d/ (дефолты пакетов) → /etc/sysctl.d/ (администраторские override'ы) → /etc/sysctl.conf (legacy, но всё ещё работает). Приоритет — алфавитный порядок файлов, последний выигрывает.

# /etc/sysctl.d/99-custom.conf
vm.swappiness = 10
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
kernel.panic = 10
fs.file-max = 2097152
fs.inotify.max_user_watches = 524288

# Применить без перезагрузки
sysctl --system                    # перечитать все файлы
sysctl -p /etc/sysctl.d/99-custom.conf  # конкретный файл

Ключевые параметры по категориям

Виртуальная память:

vm.swappiness (0-200, default 60) — агрессивность вытеснения anonymous pages в swap. 0 — swap используется только при крайней необходимости. 10 — типичная рекомендация для серверов с достаточным RAM. Для database-серверов важно держать низким, чтобы предотвратить swap'ование кэша БД.

vm.overcommit_memory — стратегия выделения памяти: 0 (default, эвристика), 1 (всегда разрешать — опасно, но нужно для Redis/fork-heavy workloads), 2 (строгий: не более swap + overcommit_ratio% от RAM).

vm.dirty_ratio / vm.dirty_background_ratio — при каком проценте «грязных» страниц начинать запись на диск. dirty_background_ratio (default 10%) — фоновая запись. dirty_ratio (default 20%) — синхронная блокировка (процесс ждёт). Снижение уменьшает burst latency, но увеличивает количество I/O-операций.

Ядро:

kernel.panic — секунды до перезагрузки после kernel panic (0 = не перезагружаться). Для серверов обычно 10-60.

kernel.pid_max — значение, на котором нумерация PID заворачивается обратно к минимуму. По умолчанию это 32768 независимо от разрядности (PID_MAX_DEFAULT); 4194304 — не дефолт, а верхняя граница, до которой параметр разрешено поднять на 64-bit (PID_MAX_LIMIT, на 32-bit потолок совпадает с дефолтом). На серверах с множеством контейнеров упереться в 32768 несложно.

kernel.core_pattern — путь для core dump'ов. Можно направить в программу (pipe): |/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h (systemd-coredump собирает и хранит дампы, доступные через coredumpctl).

Файловые системы:

fs.file-max — системный лимит на количество открытых файлов (не путать с ulimit -n — per-process лимит). На нагруженных серверах может потребоваться 1-2M.

fs.inotify.max_user_watches — максимум inotify-watches на пользователя. Фиксированные 8192 остались в прошлом: начиная с 5.11 ядро считает значение при инициализации как ~1% адресуемой памяти, делённый на стоимость одного watch, и зажимает результат в диапазон [8192, 1048576] — на машине с несколькими гигабайтами RAM дефолт получается шестизначным. IDE (VS Code, IntelliJ), файловые синхронизаторы и контейнерные рантаймы всё равно способны его исчерпать; 524288 — типичная рекомендация для developer workstation.

Kernel command line

Параметры, передаваемые ядру при загрузке через bootloader (GRUB: в /etc/default/grub, переменная GRUB_CMDLINE_LINUX). Текущие параметры: cat /proc/cmdline.

Важные параметры:

quiet — подавить сообщения ядра при загрузке. root= — корневое устройство. init= — путь к init (по умолчанию /sbin/init → systemd). systemd.unit= — target для systemd (rescue.target для однопользовательского режима). nomodeset — отключить kernel mode setting (отладка видео). iommu=pt — passthrough IOMMU (для виртуализации). mitigations=off — отключить все CPU vulnerability mitigations (performance, но небезопасно). crashkernel= — резервировать память для kdump. module_blacklist=nouveau — blacklist модуля через cmdline.

initramfs: начальный RAM-диск

initramfs (initial RAM filesystem) — cpio-архив, загружаемый в память вместе с ядром. Содержит минимальный userspace: busybox/systemd, модули для корневой ФС (файловая система, контроллер диска, LVM, LUKS), udev-правила для обнаружения устройств.

Зачем нужен: ядро не может содержать драйверы для всех возможных конфигураций хранилища. initramfs позволяет generic-ядру загрузиться на любом оборудовании: загрузить нужные модули, собрать RAID/LVM, расшифровать LUKS, смонтировать root ФС, выполнить pivot_root и передать управление реальному init.

# Просмотр содержимого
lsinitramfs /boot/initrd.img-$(uname -r)   # Debian/Ubuntu
lsinitrd /boot/initramfs-$(uname -r).img   # Fedora/RHEL

# Пересоздание
update-initramfs -u -k $(uname -r)         # Debian/Ubuntu
dracut --force                              # Fedora/RHEL
mkinitcpio -P                               # Arch

dracut (Fedora/RHEL) vs initramfs-tools (Debian) vs mkinitcpio (Arch) — три разных генератора с разной философией. dracut строит initramfs по hostonly (только модули для текущего hardware) или generic принципу. initramfs-tools Debian использует hook-систему. mkinitcpio Arch — конфигурация через /etc/mkinitcpio.conf с hooks-массивом.

Практика

# Текущее ядро и его параметры
uname -r                                    # версия
uname -a                                    # полная информация
cat /proc/version                           # версия + компилятор
cat /proc/cmdline                           # command line параметры

# Конфигурация текущего ядра
cat /boot/config-$(uname -r) | grep CONFIG_EXT4  # проверить конкретную опцию
zcat /proc/config.gz                        # если CONFIG_IKCONFIG_PROC=y

# Модули
lsmod | head -20                            # загруженные модули
find /lib/modules/$(uname -r) -name '*.ko*' | wc -l  # доступных модулей
modinfo -p e1000e                           # параметры модуля

# sysctl
sysctl -a 2>/dev/null | wc -l              # количество параметров
sysctl -a --pattern 'net.ipv4.tcp'         # фильтрация по паттерну

# Hardware info (через ядро)
lspci -k                                    # PCI-устройства и их модули
lsusb                                       # USB-устройства
cat /proc/interrupts                        # распределение прерываний
cat /proc/iomem                             # карта памяти устройств

Нюансы и trade-off'ы

Built-in vs module. Built-in (=y) — код всегда доступен, нет overhead загрузки, нет зависимости от /lib/modules. Module (=m) — загружается по требованию, может быть выгружен, обновлён без перекомпиляции ядра. Правило: драйверы, необходимые для загрузки (root FS, disk controller), должны быть built-in или в initramfs. Всё остальное — модули. Generic-ядра дистрибутивов делают почти всё модулями и используют initramfs.

Generic vs custom kernel. Generic-ядра дистрибутивов содержат ~5000+ модулей для поддержки максимально широкого набора оборудования. Custom kernel с CONFIG только для конкретной машины: быстрее загружается (меньше initramfs или его отсутствие), потенциально меньше attack surface. Trade-off: ручное обслуживание, потеря обновлений безопасности от дистрибутива. На практике оправдано для embedded, high-security или extreme performance сценариев.

sysctl: vm.overcommit_memory для Redis. Redis fork'ает процесс для background save (BGSAVE). При overcommit_memory=0 ядро может отказать в fork() если текущее потребление памяти велико, даже если copy-on-write реально использует мало памяти. Redis требует overcommit_memory=1. Это безопасно при правильном мониторинге, но на multi-tenant системе может привести к OOM.

Transparent Huge Pages (THP). CONFIG_TRANSPARENT_HUGEPAGE (enabled по умолчанию) — ядро автоматически объединяет 4KB страницы в 2MB huge pages. Хорошо для HPC, плохо для database workloads (latency spikes от compaction). MongoDB, Redis, Oracle рекомендуют отключать THP: echo madvise > /sys/kernel/mm/transparent_hugepage/enabled.

Mitigations trade-off. Spectre/Meltdown и последующие CPU-уязвимости потребовали mitigation'ов в ядре (retpoline, KPTI, SSBD и т.д.), каждый из которых стоит 1-30% производительности в зависимости от workload. mitigations=off в cmdline отключает все. Это может быть оправдано для изолированных систем (dedicated server без untrusted code), но неприемлемо для multi-tenant или cloud-среды.

Module signing и Secure Boot. Строгая цепочка доверия: UEFI → shim (подписан Microsoft) → GRUB (подписан дистрибутивом) → ядро (подписано) → модули (подписаны). Любой разрыв (загрузка неподписанного модуля) нарушает цепочку. На практике это основная проблема с NVIDIA-драйверами и ZFS (CDDL-лицензия → нет в mainline → нет подписи дистрибутива → нужен MOK).

Сравнение с FreeBSD

FreeBSD ядро — также монолитное с модулями (kld — kernel loadable module). Принципиальная разница в процессе конфигурации и обновления.

Конфигурация. FreeBSD ядро конфигурируется текстовым файлом (/usr/src/sys/amd64/conf/MYKERNEL), который include'ит GENERIC и добавляет/удаляет опции. Нет Kconfig/menuconfig — конфигурация проще, но менее интерактивна. make buildkernel KERNCONF=MYKERNEL && make installkernel — сборка и установка.

Обновление. FreeBSD обновляет ядро и base system как единое целое через freebsd-update (бинарно) или make buildworld/buildkernel (из исходников). В Linux ядро — отдельный пакет (linux-image-*, kernel-core), обновляемый пакетным менеджером независимо от userspace.

Модули. kldload/kldunload вместо modprobe/rmmod. Конфигурация автозагрузки: /boot/loader.conf (ранняя загрузка) и kld_list в /etc/rc.conf (поздняя загрузка). Нет эквивалента DKMS — out-of-tree модули (drm-kmod, virtualbox-ose-kmod) пересобираются пакетным менеджером при обновлении ядра.

sysctl. FreeBSD sysctl — не только /proc/sys аналог, но и primary API для информации о системе (hw.physmem, kern.osrelease). В Linux часть этой информации в /proc, часть в /sys, часть в sysctl — менее унифицировано. FreeBSD sysctl доступен без procfs/sysfs — через sysctl(2) syscall. Параметры задаются в /etc/sysctl.conf и /boot/loader.conf (для параметров, требующих ранней установки).

Securelevel. FreeBSD имеет kern.securelevel — глобальный уровень безопасности, ограничивающий операции ядра (нельзя загружать модули, менять флаги файлов, писать в /dev/mem). Securelevel можно только повышать во время работы. В Linux аналогом являются LSM (AppArmor, SELinux — см. статью 7) и Lockdown LSM (с ядра 5.4), который ограничивает модификацию ядра из userspace.

Источники


Связанные заметки

Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.

Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.