Модули ядра — загрузка, инспекция и блокировка
Ядро загружает поддержку драйверов и функций в виде модулей, а не компилирует всё в монолит. lsmod/modinfo инспектируют загруженные модули; modprobe загружает с разрешением зависимостей; rmmod выгружает. Автозагрузка — /etc/modules-load.d; блокировка — /etc/modprobe.d.
Ты подключаешь USB-NIC к серверу, у которого недоступен основной интерфейс. Интерфейс появляется и исчезает. В dmesg видно, как драйвер загружается, падает и загружается снова. Первопричина: параметр модуля по умолчанию задаёт «авто-определение», а коммутатор с другой стороны кабеля отключил согласование. Одна строка — modprobe r8169 speed=100 duplex=full — и интерфейс стабилизируется. Модули ядра — механизм, с помощью которого Linux избегает компиляции каждого драйвера для каждого устройства в единый монолитный бинарник. Вместо этого драйверы и функции поставляются в виде отдельных .ko-файлов, которые ядро может загружать и выгружать в рантайме. Понимание этого механизма — ключ к исправлению проблем с драйверами, настройке поведения модулей и предотвращению загрузки проблемного модуля.
После этого урока ты сможешь инспектировать загруженные модули через lsmod и modinfo, загружать и выгружать их через modprobe и rmmod, передавать параметры модулям, настраивать автозагрузку через /etc/modules-load.d/ и блокировать загрузку модуля через blacklist в /etc/modprobe.d/.
Инспекция загруженных модулей: lsmod и modinfo. lsmod читает /proc/modules и форматирует вывод для человека. modinfo запрашивает метаданные модуля из самого .ko-файла.
# Список всех загруженных модулей:
lsmod
# Module Size Used by
# nf_conntrack 172032 2 nf_nat,nf_conntrack_netlink
# ip_tables 36864 1 iptables
# dm_crypt 49152 0
# Столбцы: имя | размер в байтах | счётчик использования | зависимые
# Инспекция конкретного модуля:
modinfo ext4
# filename: /lib/modules/6.1.0-21-amd64/kernel/fs/ext4/ext4.ko
# description: Fourth Extended Filesystem
# license: GPL
# depends: mbcache,jbd2
# parm: delayed_allocation:Use delayed allocation during write (bool)
# Найти .ko-файл на диске:
modinfo -n bluetooth
# /lib/modules/6.1.0-21-amd64/kernel/net/bluetooth/bluetooth.ko
# Список параметров модуля (строки parm):
modinfo r8169 | grep parm
# parm: debug:Debug verbosity level (0=none,...,16=all) (int)Счётчик Used by показывает, безопасно ли выгрузить модуль. 0 — ничто от него не зависит; >0 — другие загруженные модули или устройства ещё используют его. rmmod откажет в выгрузке модуля с ненулевым счётчиком.
Загрузка модулей через modprobe. modprobe — правильный инструмент для загрузки модулей. В отличие от insmod (требует полного пути и игнорирует зависимости), modprobe читает базу зависимостей в /lib/modules/$(uname -r)/modules.dep и автоматически загружает пресерквизиты.
# Загрузить модуль (и его зависимости):
sudo modprobe nf_conntrack
# (нет вывода при успехе)
# Проверить загрузку:
lsmod | grep nf_conntrack
# nf_conntrack 172032 0
# Загрузить с параметрами:
sudo modprobe r8169 debug=1
# Устанавливает параметр debug для драйвера r8169 при загрузке
# Показать зависимости без загрузки:
modprobe --show-depends nf_nat
# insmod /lib/modules/.../kernel/net/netfilter/nf_conntrack.ko
# insmod /lib/modules/.../kernel/net/netfilter/nf_nat.ko
# Холостой прогон (показать, что произойдёт, без загрузки):
modprobe --dry-run --verbose tcp_bbrПараметры, переданные через modprobe, действуют только для этой загрузки. Для постоянных параметров используй /etc/modprobe.d/ (шаг 4).
Выгрузка модулей через rmmod. Выгруженный модуль освобождает память. Это используется при отладке драйверов (выгрузить, изменить параметр, загрузить снова) и когда модуль вызывает нестабильность. Выгрузка завершится ошибкой, если счётчик использования ненулевой.
# Выгрузить модуль (не работает, если от него что-то зависит):
sudo rmmod nf_conntrack
# rmmod: ERROR: Module nf_conntrack is in use by: nf_nat nf_conntrack_netlink
# → нужно сначала выгрузить зависимые модули
# modprobe -r автоматически обрабатывает цепочку зависимостей:
sudo modprobe -r nf_nat
# Выгружает nf_nat, затем nf_conntrack (если не остался других зависимостей)
# Проверить, что модуль выгружен:
lsmod | grep nf_conntrack
# (нет вывода)
# ЛОВУШКА: некоторые модули нельзя выгрузить, потому что что-то в ядре
# удерживает ссылку, которую счётчик использования не захватывает корректно.
# В этом случае 'modprobe -r' вернёт "FATAL: Module ... is in use".
# Единственный безопасный вариант — перезагрузка. Никогда не используй
# 'rmmod -f' (принудительно) на продакшн-системе — это обходит проверку
# счётчика и может вызвать kernel panic.Постоянная загрузка через /etc/modules-load.d и параметры через /etc/modprobe.d.
# Автозагрузка модуля при каждой загрузке системы:
# Создать .conf-файл в /etc/modules-load.d/ (одно имя модуля на строку):
echo "tcp_bbr" | sudo tee /etc/modules-load.d/bbr.conf
# systemd-modules-load.service читает это при загрузке
# Применить сейчас без перезагрузки:
sudo modprobe tcp_bbr
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
# Постоянные параметры модуля через /etc/modprobe.d/:
sudo tee /etc/modprobe.d/r8169.conf << 'EOF'
# Исправление для коммутатора с отключённым авто-определением
options r8169 speed=100 duplex=1
EOF
# Параметры вступают в силу при следующей загрузке модуля
# (перезагрузка или: modprobe -r r8169 && modprobe r8169)
# Блокировать проблемный модуль, чтобы он никогда не загружался:
sudo tee /etc/modprobe.d/blacklist-pcspkr.conf << 'EOF'
# Отключить драйвер PC-динамика — вызывает ложные алерты
blacklist pcspkr
EOF
# ЛОВУШКА: blacklist НЕ выгружает уже загруженный модуль.
# Он только предотвращает будущие загрузки. Если модуль уже загружен:
sudo modprobe -r pcspkr
# Затем blacklist предотвратит автозагрузку при следующей загрузке системы.
# Проверить, что blacklist действует:
cat /etc/modprobe.d/blacklist-pcspkr.conf
# blacklist pcspkr
lsmod | grep pcspkr
# (нет вывода — модуль не загружен)Тонкость: blacklist в /etc/modprobe.d/ предотвращает автоматическую загрузку через modprobe, но не мешает другому модулю явно загрузить его как зависимость. Для абсолютного запрета используй install <module> /bin/false в том же файле.
Когда blacklist не работает. Типичная загадка в продакшне: модуль добавлен в blacklist, но всё равно загружается. Виновник — обычно initramfs.
# initramfs — это раннее загрузочное окружение, монтирующее реальную корневую ФС.
# У него собственная копия конфигурации модулей. Если твой blacklist туда
# не попал — модуль загружается до монтирования /etc/modprobe.d/.
# После создания файла blacklist пересобери initramfs:
sudo update-initramfs -u
# update-initramfs: Generating /boot/initrd.img-6.1.0-21-amd64
# Перезагрузиться и проверить:
lsmod | grep <имя-модуля>
# (должно быть пусто)
# Если модуль нужен самому initramfs (например, драйвер файловой системы),
# его нельзя безопасно занести в blacklist — система не загрузится.
# В этом случае используй параметр ядра:
# Добавить в /etc/default/grub GRUB_CMDLINE_LINUX: module_blacklist=nouveau
# Затем: sudo update-grubСценарий: драйвер Nouveau вызывает kernel panic; переключиться на blacklist, чтобы проприетарный драйвер взял управление.
# Шаг 1: убедиться, что nouveau загружен и вызывает проблемы:
lsmod | grep nouveau
# nouveau 2166784 0
dmesg | tail -20 | grep -i nouveau
# [ 15.321] nouveau 0000:01:00.0: bus: MMIO read of 00000000 FAULT ...
# Шаг 2: попытаться выгрузить сейчас:
sudo modprobe -r nouveau
# modprobe: FATAL: Module nouveau is in use.
# Что-то (X-сервер, GDM) удерживает ссылку.
# Шаг 3: занести в blacklist, чтобы не загружался при следующей загрузке:
sudo tee /etc/modprobe.d/blacklist-nouveau.conf << 'EOF'
blacklist nouveau
options nouveau modeset=0
EOF
# Шаг 4: пересобрать initramfs, чтобы blacklist применился до монтирования /:
sudo update-initramfs -u
# update-initramfs: Generating /boot/initrd.img-6.1.0-21-amd64
# Шаг 5: перезагрузиться:
sudo reboot
# Шаг 6: после перезагрузки убедиться, что nouveau отсутствует:
lsmod | grep nouveau
# (нет вывода — blacklist работает)
# Шаг 7: установить и загрузить проприетарный драйвер (например, NVIDIA):
sudo apt install nvidia-driver
# Инсталлятор сам обрабатывает modprobe для nvidia и nvidia_uvm
lsmod | grep nvidia
# nvidia_uvm 65536 0
# nvidia 21397504 1 nvidia_uvmКлючевой урок: одного blacklist недостаточно, когда модуль загружается из initramfs. update-initramfs -u — шаг, который люди пропускают, получая «я добавил в blacklist, но он всё равно загружается».
▸Частая ошибка
Использовать rmmod -f (принудительно) на продакшн-системе. Флаг -f обходит проверку счётчика использования и удаляет модуль, даже если другой код держит на него ссылки. Результат — ядро пытается вызвать функции в памяти, которой больше нет: kernel panic или молчаливое повреждение данных. Практически никогда нет законной причины использовать -f на работающей продакшн-системе. Правильный подход: найти, что использует модуль, через lsmod (третий столбец показывает зависимые), сначала выгрузить зависимые, затем выгрузить сам модуль чисто.
▸Почему это работает
На macOS нет модулей ядра в смысле Linux. macOS использует расширения ядра (kexts) на старых версиях и DriverKit (драйверы в пользовательском пространстве) на новых. Команды kextload/kextunload существуют, но выводятся из употребления. Кроме того, System Integrity Protection (SIP) на современных Mac запрещает загрузку неподписанных kexts. Модульная система Linux значительно более открытая — любой пользователь root может загрузить любой скомпилированный .ko-файл, что одновременно является преимуществом в гибкости и соображением безопасности (скомпрометированный root может загрузить руткит). Linux Security Modules (SELinux, AppArmor) могут ограничивать загрузку модулей, но по умолчанию система открыта.
Ты добавляешь `blacklist pcspkr` в /etc/modprobe.d/blacklist.conf. После следующей перезагрузки `lsmod | grep pcspkr` всё равно показывает загруженный модуль. Какова наиболее вероятная причина?
Ядро поставляет драйверы и функции в виде отдельных .ko-файлов вместо единого монолита, позволяя загрузку и выгрузку в рантайме. Инспектируй загруженные модули через lsmod (список со счётчиком использования и зависимыми) и modinfo (метаданные, параметры, зависимости). Загружай через modprobe (автоматически разрешает цепочку зависимостей); передавай параметры как modprobe <name> param=value. Выгружай через modprobe -r <name> (обрабатывает зависимые) или rmmod (один модуль, отказывает при счётчике > 0 — никогда не используй -f в продакшне). Автозагрузка при старте: добавляй имена модулей в /etc/modules-load.d/<name>.conf. Постоянные параметры: options <module> param=value в /etc/modprobe.d/<name>.conf. Blacklist: blacklist <module> в /etc/modprobe.d/; если модуль в initramfs — также запускай sudo update-initramfs -u, иначе blacklist не действует на ранней стадии загрузки.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.