Обзор
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.
Источники
- Kernel Build System (Kbuild) — официальная документация Kbuild
- Kconfig Language — спецификация языка Kconfig
- The Linux Kernel Module Programming Guide — практическое руководство по модулям
- Documentation for /proc/sys/kernel/ — sysctl параметры ядра
- Documentation for /proc/sys/vm/ — sysctl параметры виртуальной памяти
- Documentation for /proc/sys/net/ — sysctl сетевые параметры
- Kernel parameters — полный список параметров command line
- dracut — генератор initramfs (Fedora/RHEL)
- Exploring Kconfig/kbuild — Opensource.com — обзор build system
- FreeBSD Handbook: Configuring the FreeBSD Kernel — для сравнения
Связанные заметки
- Linux — обновление и управление загрузкой — initramfs, параметры загрузки, смена ядра
- Linux — мониторинг и отладка — наблюдение за ядром через perf и eBPF
- Linux — справочник команд: фреймворки трассировки — ftrace, kprobes, BPF-инструменты
- FreeBSD — sysctl и конфигурация ядра — сборка ядра и sysctl во FreeBSD, для сравнения
- Linux — сводный cheatsheet — быстрая справка по модулям и sysctl
- Магия как метафора: язык и культура Unix — язык комментариев и имён в исходниках ядра; «You are not expected to understand this»
- Локальный инференс LLM — параметры модулей amdgpu и TTM, которыми задаётся объём памяти для GPU и APU
- Устройство ОС — план цикла статей — цикл о внутреннем устройстве ядра: карта памяти, стеки, вход в ядро
- Страничная трансляция: таблицы, TLB, разрядность — механизм, которым управляют параметры загрузки про уровни трансляции и большие страницы
- Ядро как программа: freestanding, компоновка, ABI, выбор языка — то же ядро изнутри: freestanding-окружение, скрипт компоновки, ABI
- Карта памяти ядра: половины, прямое отображение, KASLR — что стоит за параметрами
nopti,nokaslrиrodataи почему модули лежат там, где лежат - Загрузка: от питания до первой инструкции ядра — что происходит до первой инструкции ядра и зачем нужен initramfs
- Структура ядра: монолит, микроядро и что между — почему загружаемый модуль не даёт изоляции и чем модульность отличается от микроядерности
Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.
Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.