Сейчас каждая операционная система имеет разделение на пользовательский режим и режим ядра, в этой статье разбирается ядро 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_hcd, rmmod 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.