Ядро Linux и модули ядра

Сейчас каждая операционная система имеет разделение на пользовательский режим и режим ядра, в этой статье разбирается ядро Linux и его модули.















Ядро операционной системы




Все процессы, работающие в системе, работают либо в режиме ядра, либо в пользовательском режиме. Процессы, работающие в режиме ядра получают больше прав, чем в пользовательском режиме. В режиме ядра работает само ядро и могут работать другие подсистемы.




Ядро является самой главной частью операционной системы, так как в него закладываются важнейшие функции:




  • связь с оборудованием, и предоставление ресурсов компьютера всем процессам в системе;



  • управление всеми процессами в системе;



  • в зависимости от типа ядра, у него могут быть и другие функции. Либо эти функции могут быть вынесены из ядра, но работать в режиме ядра. Или могут вообще быть вынесены из режима ядра в пользовательский режим.




Вообще, ядро можно рассматривать как главную программу, которая устанавливает правила, по которым работают остальные программы. Все в системе должно работать по правилам ядра. А ядро подчиняется правилам, которые диктует оборудование (архитектура процессора, оперативная память, системная плата).




Типы ядер




Существуют следующие типы ядер:




  • Монолитные — очень много функций, помимо основных, заложено в ядро. Это и управление файловой системой, работа с драйверами оборудования, работа с сетью. Это увеличивает размер ядра, усложняет его, делает ядро менее безопасным, но упрощает обмен между процессами, создает более легкий доступ к оборудованию. Ядро Linux является именно таким ядром.



  • Микроядра — взаимодействуют только с процессором, и управляют процессами в системе. Все остальное вынесено из ядра и работает в режиме пользователя.



  • Гибридные ядра — пытаются как можно больше вынести из ядра, но взаимодействуют не только с процессором. Ядро Windows является гибридным.




Особенность ядра Linux




В Linux все является файлом (файл, каталог, сокет, оборудование). Об этом я уже писал здесь.




Ядро написано на языке C и является переносимым. Оно реализует многозадачность и многопоточность, виртуальную память и многое другое. Также оно соответствует стандартам POSIX, это означает что программа написанная по стандартам POSIX будет работать на всех дистрибутивах Linux.




Официальный сайт ядра Linux: https://www.kernel.org/. Здесь можно найти исходники и списки изменений разных версий ядра.




Создатель и основной руководитель ядра Linux — Линус Торвальдс:




Линус Торвальдс
Линус Торвальдс




Файлы ядра




Файл ядра находится в каталоге /boot и начинается со слова vmlinuz, а дальше идет версия:




alex@deb-11:~$ ls -l /boot/vmlinuz*
-rw-r--r-- 1 root root 6962816 авг 13 16:25 /boot/vmlinuz-5.10.0-17-amd64
-rw-r--r-- 1 root root 6962016 сен  2 16:54 /boot/vmlinuz-5.10.0-18-amd64

alex@ubu-22:~$ ls -l /boot/vmlinuz*
lrwxrwxrwx 1 root root       25 ноя 16 07:00 /boot/vmlinuz -> vmlinuz-5.15.0-53-generic
-rw------- 1 root root 11543392 окт 13 07:49 /boot/vmlinuz-5.15.0-52-generic
-rw------- 1 root root 11548224 окт 17 18:41 /boot/vmlinuz-5.15.0-53-generic
lrwxrwxrwx 1 root root       25 ноя 16 07:00 /boot/vmlinuz.old -> vmlinuz-5.15.0-52-generic




По умолчанию Debian и Ubuntu хранят старые ядра после обновления, поэтому здесь находятся несколько файлов.




Посмотрим что лежит еще в каталоге /boot:




alex@deb-11:~$ ls -l /boot/
итого 70660
-rw-r--r-- 1 root root   236286 авг 13 16:25 config-5.10.0-17-amd64
-rw-r--r-- 1 root root   236286 сен  2 16:54 config-5.10.0-18-amd64
drwxr-xr-x 5 root root     4096 сен 13 12:23 grub
-rw-r--r-- 1 root root 28960621 сен  9 10:49 initrd.img-5.10.0-17-amd64
-rw-r--r-- 1 root root 28979132 сен 12 15:00 initrd.img-5.10.0-18-amd64
-rw-r--r-- 1 root root       83 авг 13 16:25 System.map-5.10.0-17-amd64
-rw-r--r-- 1 root root       83 сен  2 16:54 System.map-5.10.0-18-amd64
-rw-r--r-- 1 root root  6962816 авг 13 16:25 vmlinuz-5.10.0-17-amd64
-rw-r--r-- 1 root root  6962016 сен  2 16:54 vmlinuz-5.10.0-18-amd64

alex@ubu-22:~$ ls -l /boot/
total 244772
-rw-r--r-- 1 root root    261861 окт 13 07:40 config-5.15.0-52-generic
-rw-r--r-- 1 root root    261837 окт 17 18:36 config-5.15.0-53-generic
drwxr-xr-x 5 root root      4096 ноя 16 07:00 grub
lrwxrwxrwx 1 root root        28 ноя 16 07:00 initrd.img -> initrd.img-5.15.0-53-generic
-rw-r--r-- 1 root root 107247090 ноя  9 06:19 initrd.img-5.15.0-52-generic
-rw-r--r-- 1 root root 107269841 ноя 16 07:00 initrd.img-5.15.0-53-generic
lrwxrwxrwx 1 root root        28 ноя 16 07:00 initrd.img.old -> initrd.img-5.15.0-52-generic
-rw------- 1 root root   6249017 окт 13 07:40 System.map-5.15.0-52-generic
-rw------- 1 root root   6250186 окт 17 18:36 System.map-5.15.0-53-generic
lrwxrwxrwx 1 root root        25 ноя 16 07:00 vmlinuz -> vmlinuz-5.15.0-53-generic
-rw------- 1 root root  11543392 окт 13 07:49 vmlinuz-5.15.0-52-generic
-rw------- 1 root root  11548224 окт 17 18:41 vmlinuz-5.15.0-53-generic
lrwxrwxrwx 1 root root        25 ноя 16 07:00 vmlinuz.old -> vmlinuz-5.15.0-52-generic




  • vmlinuz — ядро;



  • initrd.img — образ стартовой файловой системы, которая необходима чтобы запустить ядро;



  • System.map — файл для управления памятью;



  • config — файл параметров с которыми собрано текущее ядро;



  • Каталог grub — файлы загрузчика grub, про него я рассказывал здесь.




Модули ядра




Так как ядро в Linux — монолитное. То все драйвера должны быть включены в ядро. Вначале так и было, но когда возможного оборудования стало слишком много, и всё многообразие драйверов не могли поместить в одно ядро, придумали динамически подгружаемые модули ядра.




Сегодня ядро является минимальным, и поддержка устройства осуществляется с помощью динамически подгружаемых модулей ядра.




Вот размер ядра:




alex@deb-11:~$ du -h /boot/vmlinuz-5.10.0-18-amd64
6,7M    /boot/vmlinuz-5.10.0-18-amd64

alex@ubu-22:~$ du -h /boot/vmlinuz-5.15.0-53-generic
12M     /boot/vmlinuz-5.15.0-53-generic




Базовый набор модулей обычно включается в initrd.img, который распаковывается в момент загрузки системы. После того, как система будет загружена, ядро прощупывает систему дополнительно и подгружает необходимые модули устройств, так обнаруживаются новые устройства.




Хоть модули динамически подгружаются, при обнаружении устройства, их можно подгружать и выгружать вручную.




Использование lsmod




Команда lsmod берет информацию из /proc/modules чтобы вывести список уже загруженных модулей:




$ lsmod | head -n 20
Module                        Size  Used by
binfmt_misc                   24576  1
snd_hda_codec_generic         98304  2
ledtrig_audio                 16384  1 snd_hda_codec_generic
snd_hda_intel                 57344  0
snd_intel_dspcfg              28672  1 snd_hda_intel
soundwire_intel               45056  1 snd_intel_dspcfg
soundwire_generic_allocation  16384  1 soundwire_intel
snd_soc_core                  319488  1 soundwire_intel
snd_compress                  32768  1 snd_soc_core
soundwire_cadence             36864  1 soundwire_intel
snd_hda_codec                176128  2 snd_hda_codec_generic,snd_hda_intel
snd_hda_core                 110592  3 snd_hda_codec_generic,snd_hda_intel,snd_hda_codec
qxl                           77824  0
snd_hwdep                     16384  1 snd_hda_codec
drm_ttm_helper                16384  1 qxl
soundwire_bus                 94208  3 soundwire_intel,soundwire_generic_allocation,soundwire_cadence
joydev                        28672  0
ttm                          114688  2 qxl,drm_ttm_helper
snd_pcm                      143360  6 snd_hda_intel,snd_hda_codec,soundwire_intel,snd_compress,snd_soc_core,snd_hda_core




А если вам нужно получить список всех модулей ядра, а не только загруженных, то можете воспользоваться следующей командой:




$ find /lib/modules/$(uname -r) -name *.ko




Вывод предыдущей команды будет очень большим, например в Ubuntu сейчас более 6040 подгружаемых модулей.




Использование modinfo




Команда modinfo нужна для получения информации о модуле. Вы можете указать полное имя файла или имя модуля.




Для выполнения этой команды в Ubuntu не обязательно использовать sudo, а в Debian нужно:




alex@ubu-22:~$ modinfo vfat
name:           vfat
filename:       (builtin)
author:         Gordon Chaffee
description:    VFAT filesystem support
license:        GPL
file:           fs/fat/vfat
alias:          fs-vfat

alex@deb-11:~$ sudo modinfo vfat
filename:       /lib/modules/5.10.0-18-amd64/kernel/fs/fat/vfat.ko
author:         Gordon Chaffee
description:    VFAT filesystem support
license:        GPL
alias:          fs-vfat
depends:        fat
retpoline:      Y
intree:         Y
name:           vfat
vermagic:       5.10.0-18-amd64 SMP mod_unload modversions
sig_id:         PKCS#7
signer:         Debian Secure Boot CA
sig_key:        32:A0:28:7F:84:1A:03:6F:A3:93:C1:E0:65:C4:3A:E6:B2:42:26:43
sig_hashalgo:   sha256
signature:      A0:1A:81:0B:41:A4:6C:82:1A:2F:85:91:BD:E3:51:69:CA:95:4E:EF:
                BB:29:9E:D5:DF:79:26:EC:85:2E:B7:2E:53:E7:4F:16:F5:00:C3:F2:
                C2:F0:85:51:8F:C3:2A:51:EF:0E:85:32:6F:7D:8D:59:6D:37:5F:7B:
                76:71:8A:C4:D3:96:39:3E:F7:EE:E5:59:ED:79:65:65:51:4C:3F:F4:
                F6:05:C7:61:D6:0D:91:B3:71:19:CA:81:B8:D6:90:CA:21:44:6A:FC:
                B0:B2:AE:11:7D:D9:EA:E5:E9:93:6B:A0:30:F2:20:95:97:84:E4:81:
                CA:26:09:3E:78:1A:B6:BB:21:7B:DC:B2:18:C6:2B:C9:1B:8F:60:7A:
                D3:8B:CB:21:15:C1:E5:88:75:06:07:DC:DC:B0:E9:5F:DF:C5:6F:20:
                DE:39:EA:7D:13:83:D1:92:7A:3A:0C:34:27:F2:50:43:5B:EA:68:E8:
                25:B2:A7:81:49:38:E7:8A:1C:25:74:B0:00:DC:CB:5A:52:D7:07:E0:
                32:12:D6:ED:0B:CC:90:49:00:5B:DB:7C:5B:5C:9F:44:C1:DF:51:EC:
                19:A7:96:2F:5A:7C:BF:E5:C9:9E:AB:08:2D:05:83:18:B8:F0:87:7E:
                03:C3:53:64:21:0D:35:54:B4:04:EF:39:32:71:0E:19




Выше вы можете заметить разницу. В Ubuntu вместо имени файла написано — (builtin). А в Debian/lib/modules/5.10.0-18-amd64/kernel/fs/fat/vfat.ko. Если вы вместо имени видите (builtin), значит этот модуль встроен в ядро.




Утилита modinfo показывает следующую информацию:




  • filename — полный путь к файлу (если это не встроенный в ядро модуль);



  • author — информация о создателе;



  • description — описание;



  • license — лицензия;



  • alias — псевдоним (их может быть несколько);



  • depends — зависимость, то есть без этого модуля, он не загрузится. Зависимостей может быть несколько. (не бывает для встроенных в ядро модулей);



  • retpoline — указывает, что модуль построен с поддержкой защиты от Spectre;



  • intree — все модули ядра начинают разработку как out-of-tree. Как только модуль принимается для включения в ядро, он становится модулем in-tree. Модули без этого флага (установленного в N) могут испортить ядро;



  • name — имя модуля;



  • vermagic — это строка используется для проверки, был ли модуль ядра скомпилирован для конкретной версии ядра или нет (не бывает для встроенных в ядро модулей);



  • parm — если у модуля есть параметры, то они записываются сюда.




Вы можете использовать опцию -F для ограничения вывода по конкретному полю:




alex@ubu-22:~$ modinfo -F description vfat
VFAT filesystem support




Если вы не укажете полное имя файла, modinfo ищет модуль в /lib/modules/<версия_ядра>/kernel




Версию вашего ядра можно получить командой uname -r:




alex@deb-11:~$ uname -r
5.10.0-18-amd64

alex@ubu-22:~$ uname -r
5.15.0-52-generic




Для поиска модулей ядра можно воспользоваться такой командой:




alex@deb-11:~$ ls /lib/modules/`uname -r`/kernel
arch  block  crypto  drivers  fs  lib  mm  net  sound  virt

alex@ubu-22:~$ ls /lib/modules/`uname -r`/kernel
arch   crypto   fs      lib  net      sound   v4l2loopback
block  drivers  kernel  mm   samples  ubuntu  zfs




Вы можете найти некоторые простые текстовые файлы в каталоге:




$ ls /lib/modules/`uname -r`
build          modules.alias.bin          modules.builtin.modinfo  modules.order        vdso
initrd         modules.builtin            modules.dep              modules.softdep
kernel         modules.builtin.alias.bin  modules.dep.bin          modules.symbols
modules.alias  modules.builtin.bin        modules.devname          modules.symbols.bin




  • modules.dep — перечислены зависимости;



  • modules.alias — перечислены псевдонимы;



  • modules.builtin — содержит модули, которые встроены в ядро. К ним относятся драйверы необходимые для основных функциональных возможностей в большинстве систем.




Если вы заходите получить имя файла модуля с помощью modinfo а модуль окажется встроенным, то попробуйте поискать информацию в файле modules.builtin:




alex@deb-11:~$ sudo modinfo processor -F filename
(builtin)

alex@deb-11:~$ grep processor /lib/modules/`uname -r`/modules.builtin
kernel/drivers/acpi/processor.ko

alex@ubu-22:~$ modinfo processor -F filename
(builtin)
(builtin)

alex@ubu-22:~$ grep processor /lib/modules/`uname -r`/modules.builtin
kernel/drivers/acpi/processor.ko
kernel/drivers/xen/xen-acpi-processor.ko




Использование modprobe




Ваша система использует модули ядра для управления устройствами. Некоторые из этих модулей имеют зависимости. Таким образом, загрузка нужных модулей в правильном порядке имеет важное значение.




Раньше использовались команды: insmod — для загрузки модуля, и rmmod — для извлечения модуля.




Команда modprobe заменила insmod и rmmod, при этом modprobe следит за зависимостями и выгружает или загружает модули с их учётом.




Я буду использовать модуль uhci_hcd для этого примера. Используйте modinfo, чтобы увидеть зависимости этого модуля:




alex@ubu-22:~$ modinfo uhci_hcd
name:           uhci_hcd
filename:       (builtin)
license:        GPL
file:           drivers/usb/host/uhci-hcd
description:    USB Universal Host Controller Interface driver
author:         Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, Alan Stern
softdep:        pre: ehci_pci
parm:           ignore_oc:ignore hardware overcurrent indications (bool)
parm:           debug:Debug level (int)


alex@deb-11:~$ sudo modinfo uhci_hcd
filename:       /lib/modules/5.10.0-18-amd64/kernel/drivers/usb/host/uhci-hcd.ko
license:        GPL
description:    USB Universal Host Controller Interface driver
author:         Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, Alan Stern
softdep:        pre: ehci_pci
alias:          pci:v*d*sv*sd*bc0Csc03i00*
depends:        usbcore,usb-common
retpoline:      Y
intree:         Y
name:           uhci_hcd
vermagic:       5.10.0-18-amd64 SMP mod_unload modversions
sig_id:         PKCS#7
signer:         Debian Secure Boot CA
sig_key:        32:A0:28:7F:84:1A:03:6F:A3:93:C1:E0:65:C4:3A:E6:B2:42:26:43
sig_hashalgo:   sha256
signature:      0A:D2:BF:AF:B5:35:14:B4:7D:BC:73:6F:06:66:9A:F1:5D:94:67:99:
                CA:72:89:45:7C:3E:95:B1:00:19:F3:59:D6:D2:74:82:E8:AA:85:F8:
                60:A0:AB:CB:98:E1:A5:E4:7F:A6:3D:48:29:9B:4C:01:33:61:61:C0:
                A3:E5:80:B3:E6:3A:78:B0:F1:DB:D0:AF:A6:CA:41:EA:B2:59:EB:E6:
                4B:0C:80:69:3E:06:7A:BB:E9:DD:16:C6:B4:D2:78:10:68:D2:F0:0F:
                93:62:18:80:71:D3:42:3D:81:D6:3E:48:B5:85:FE:FB:77:C0:03:68:
                23:50:9B:99:F1:F8:8C:AF:9F:93:2D:28:16:AF:36:75:1E:72:54:AF:
                A6:3A:DD:43:32:6C:4C:39:1C:66:B9:77:B2:E2:9C:76:45:D7:0D:85:
                AE:38:9B:4A:69:00:B1:AA:92:5A:85:86:89:04:82:F7:DA:40:D4:2E:
                9D:D6:B9:AE:5C:44:01:6C:B6:95:8E:3C:02:06:18:D0:E1:D2:6A:18:
                E4:39:FF:0C:1D:62:C5:2D:16:C6:51:2F:24:B8:3E:DB:3A:8D:11:C6:
                48:F9:AC:A7:C2:83:95:88:D3:28:EC:C1:E9:6A:B0:B1:54:5E:76:E6:
                A6:00:1A:79:44:66:61:BB:9B:8C:7B:B4:B3:F6:EA:8C
parm:           ignore_oc:ignore hardware overcurrent indications (bool)
parm:           debug:Debug level (int)




Как видите, в Ubuntu этот модуль встроен в ядро. А в Debian это подгружаемый модуль ядра. Поэтому дальнейшие действия я буду выполнять в Debian.




Найдём зависимости для uhci_hcd:




$ sudo modinfo -F depends uhci_hcd
usbcore,usb-common




Как вы видите, этот модуль имеет зависимость от usbcore. А он зависит от usb-common:




$ sudo modinfo -F depends usbcore
usb-common




На этом зависимости кончаются:




$ sudo modinfo -F depends usb-common




Если вы хотите, вручную загрузить или выгрузить драйвер, используйте modprobe с опцией:




  • -i, чтобы загрузить модуль;



  • -r, чтобы выгрузить модуль;



  • -n чтобы показать, что будет сделано, не делая это;



  • а опция -v покажет подробную информацию.




Например:




$ sudo modprobe -nrv uhci_hcd
rmmod uhci_hcd
rmmod ehci_hcd




То есть, при выгрузке uhci_hcd выгрузится еще и ehci_hcd. Мы можем выполнить эти две команды (rmmod uhci_hcdrmmod ehci_hcd), или выполнить одну modprobe.




Давайте выгрузим этот модуль:




$ sudo modprobe -rv uhci_hcd
rmmod uhci_hcd
rmmod ehci_hcd
rmmod usbcore
rmmod usb_common




И загрузим модули обратно:




$ sudo modprobe -iv uhci_hcd
insmod /lib/modules/5.10.0-18-amd64/kernel/drivers/usb/common/usb-common.ko
insmod /lib/modules/5.10.0-18-amd64/kernel/drivers/usb/core/usbcore.ko
insmod /lib/modules/5.10.0-18-amd64/kernel/drivers/usb/host/uhci-hcd.ko

$ sudo modprobe -iv ehci-hcd
insmod /lib/modules/5.10.0-18-amd64/kernel/drivers/usb/host/ehci-hcd.ko




Вы не можете выгрузить модуль если он используется или если этот модуль встроен в ядро. Именно поэтому я не стал приводить пример на Ubuntu:




$ sudo modprobe -rv uhci_hcd
modprobe: FATAL: Module uhci_hcd is builtin




Параметры модулей




Некоторые модули имеют параметры. Например, драйвер устройства должен знать, какие IRQ или порт I/O использовать. Следующий пример показывает информацию о модуле usbhid, который имеет несколько таких параметров:




$ modinfo -F parm usbhid
mousepoll:Polling interval of mice (uint)
jspoll:Polling interval of joysticks (uint)
kbpoll:Polling interval of keyboards (uint)
ignoreled:Autosuspend with active leds (uint)
quirks:Add/modify USB HID quirks by specifying  quirks=vendorID:productID:quirks where vendorID, productID, and quirks are all in 0x-prefixed hex (array of charp)




Чтобы изменить параметры, нужно указать их при загрузке модуля в командной строке modprobe.




Автозагрузка модулей ядра




Бывает ситуация, когда модуль ядра не загружается при подключении устройства. И его нужно загрузить вручную, например:




$ sudo modprobe 8021q




Проверить, загружен модуль или нет можно так:




$ sudo lsmod | grep 8021q
8021q                  40960  0
garp                   16384  1 8021q
mrp                    20480  1 8021q




Ну а чтобы в дальнейшем, после перезагрузки, этот модуль загружался, нужно добавить его название в конфиг в каталог /etc/modules-load.d:




$ sudo nano /etc/modules-load.d/8021q.conf
8021q




После этого можно перезагрузиться и проверить, что модуль загружен автоматически.




Также в Debian и Ubuntu для автозагрузки модуля ядра, можно добавить имя модуля в файл /etc/modules.









Итог




Мы узнали что такое ядро операционной системы и особенности Linux ядра. Узнали в каком файле находится ядро Linux и познакомились с дополнительными файлами, которые нужны для загрузки ядра.




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




Узнали как получить информацию о модуле ядра используя команду modinfo, а также как получить список модулей в системе используя lsmod.










2022-11-22T14:51:17
Администрирование Linux