Не выгружать коды ядра и драйверов из оперативной памяти

Обновлено: 02.07.2024

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

Различия между модулями ядра и пользовательскими программами

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

Различия в выполнении между модулями ядра и пользовательскими программами

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

Модули ядра имеют отдельное адресное пространство. Модуль работает в пространстве ядра. Приложение работает в пользовательском пространстве. Системное ПО защищено от пользовательских программ. Пространство ядра и пользовательское пространство имеют свои собственные адресные пространства памяти. Важную информацию об адресных пространствах см. в разделе Адресные пространства пользователя и ядра на компьютерах x86 и SPARC.

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

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

Модули ядра могут быть прерваны. Несколько процессов могут одновременно запрашивать ваш драйвер. Обработчик прерывания может запросить ваш драйвер в то же время, когда ваш драйвер обслуживает системный вызов. В симметричной многопроцессорной (SMP) системе ваш драйвер может выполняться одновременно на нескольких ЦП.

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

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

Структурные различия между модулями ядра и пользовательскими программами

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

Модули ядра не определяют основную программу. Модули ядра, включая драйверы устройств, не имеют функции main(). Вместо этого модуль ядра представляет собой набор подпрограмм и данных. Драйвер устройства — это модуль ядра, формирующий программный интерфейс для устройства ввода-вывода (I/O). Подпрограммы в драйвере устройства предоставляют точки входа в устройство. Ядро использует атрибут номера устройства, чтобы определить местонахождение подпрограммы open() и других подпрограмм правильного драйвера устройства. Дополнительные сведения о точках входа см. в разделе Драйверы устройств. Описание номеров устройств см. в разделе Номера устройств.

Модули ядра связаны только с ядром. Модули ядра не связываются с теми же библиотеками, с которыми связываются пользовательские программы. Единственные функции, которые может вызывать модуль ядра, — это функции, экспортируемые ядром. Если ваш драйвер ссылается на символы, которые не определены в ядре, ваш драйвер скомпилируется, но не загрузится. Модули драйверов ОС Oracle Solaris должны использовать предписанные интерфейсы DDI/DKI (интерфейс драйвера устройства, интерфейс драйвера-ядра). При использовании этих стандартных интерфейсов вы можете выполнить обновление до новой версии Oracle Solaris или перейти на новую платформу без перекомпиляции драйвера. Дополнительные сведения о DDI см. в разделе «Интерфейсы DDI/DKI» в статье «Написание драйверов устройств». Модули ядра могут зависеть от других модулей ядра, используя параметр -N во время редактирования ссылки. См. справочную страницу ld(1) для получения дополнительной информации.

Модули ядра используют разные заголовочные файлы. Для модулей ядра требуется другой набор заголовочных файлов, чем для пользовательских программ. Необходимые заголовочные файлы перечислены на странице руководства для каждой функции.См. раздел справочных страниц 9: Функции ядра DDI и DKI для функций DDI/DKI, раздел справочных страниц 9: Точки входа драйвера DDI и DKI для точек входа и раздел справочных страниц 9: Свойства и структуры данных DDI и DKI для структур. Модули ядра могут включать файлы заголовков, совместно используемые пользовательскими программами, если интерфейсы пользователя и ядра в таких файлах общих заголовков определены условно с помощью макроса _KERNEL.

Модули ядра должны избегать глобальных переменных. Избегать глобальных переменных в модулях ядра даже важнее, чем избегать глобальных переменных в пользовательских программах. Насколько это возможно, объявляйте символы как статические. Если вам необходимо использовать глобальные символы, дайте им префикс, уникальный в пределах ядра. Использование этого префикса для закрытых символов в модуле также является хорошей практикой.

Модули ядра можно настроить для аппаратного обеспечения. Модули ядра могут назначать регистры процессов определенным ролям. Код ядра можно оптимизировать для конкретного процессора.

Модули ядра могут загружаться динамически. Набор подпрограмм и данных, составляющих драйвер устройства, может быть скомпилирован в один загружаемый модуль объектного кода. Затем этот загружаемый модуль может быть статически или динамически связан с ядром и отсоединен от ядра. Вы можете добавлять функциональные возможности в ядро, пока система запущена и работает. Вы можете протестировать новые версии драйвера без перезагрузки системы.

Различия в передаче данных между модулями ядра и пользовательскими программами

Передача данных между устройством и системой обычно происходит медленнее, чем передача данных внутри ЦП. Поэтому драйвер обычно приостанавливает выполнение вызывающего потока до завершения передачи данных. Пока поток, вызвавший драйвер, приостановлен, ЦП может выполнять другие потоки. Когда передача данных завершена, устройство отправляет прерывание. Драйвер обрабатывает прерывание, которое драйвер получает от устройства. Затем драйвер сообщает ЦП о возобновлении выполнения вызывающего потока. См. главу 8 «Обработчики прерываний» в статье «Написание драйверов устройств».

Драйверы должны работать с адресами пользовательских процессов (виртуальными), системными адресами (ядра) и адресами шины ввода-вывода. Драйверы иногда копируют данные из одного адресного пространства в другое адресное пространство, а иногда просто манипулируют таблицами отображения адресов. См. «Архитектуры шины» в разделе «Написание драйверов устройств» .

Адресные пространства пользователя и ядра на компьютерах x86 и SPARC

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

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

Драйвер, работающий на компьютере с архитектурой x86, может не работать на компьютере со SPARC, поскольку драйвер может получить доступ к недопустимому адресу.

Не обращайтесь к данным пользователя напрямую. Драйвер, который напрямую обращается к адресному пространству пользователя, использует плохую практику программирования. Такой драйвер не является переносимым и не поддерживается. Используйте подпрограммы ddi_copyin(9F) и ddi_copyout(9F) для передачи данных в адресное пространство пользователя и из него. Эти две подпрограммы являются единственными поддерживаемыми интерфейсами для доступа к пользовательской памяти. В разделе «Изменение данных, хранящихся в памяти ядра» показан пример драйвера, использующего ddi_copyin(9F) и ddi_copyout(9F).

Системный вызов mmap(2) сопоставляет страницы памяти между адресным пространством процесса и файлом или объектом общей памяти. В ответ на системный вызов mmap(2) система вызывает точку входа devmap(9E) для сопоставления памяти устройства с пользовательским пространством. Затем эта информация доступна для прямого доступа пользовательских приложений.

Драйверы устройств

Драйвер устройства – это загружаемый модуль ядра, который управляет передачей данных между устройством и ОС. Загружаемые модули загружаются во время загрузки или по запросу и выгружаются по запросу. Драйвер устройства — это набор подпрограмм C и структур данных, к которым могут обращаться другие модули ядра. Эти подпрограммы должны использовать стандартные интерфейсы, называемые точками входа. Благодаря использованию точек входа вызывающие модули защищены от внутренних данных драйвера. Дополнительную информацию о точках входа см. в разделе «Точки входа в драйверы устройств» в разделе «Написание драйверов устройств».

Драйвер устройства объявляет общие точки входа в своей структуре dev_ops(9S). Драйвер объявляет точки входа для подпрограмм, связанных с символьными или блочными данными, в своей структуре cb_ops(9S). Некоторые точки входа и структуры, общие для большинства драйверов, показаны на следующей диаграмме.

Рис. 1–1 Типичные точки входа в драйвер устройства

Операционная система Oracle Solaris предоставляет множество точек входа для драйверов. Для разных типов устройств требуются разные точки входа в драйвер. На следующей диаграмме показаны некоторые доступные точки входа, сгруппированные по типу драйвера. Ни один драйвер устройства не будет использовать все точки входа, показанные на схеме.

Рис. 1–2. Точки входа для разных типов драйверов

В ОС Oracle Solaris драйверы могут управлять физическими устройствами, такими как дисковые накопители, или программными (псевдо) устройствами, такими как шинные нексусные устройства или RAM-диски. В случае аппаратных устройств драйвер устройства взаимодействует с аппаратным контроллером, который управляет устройством. Драйвер устройства защищает уровень пользовательского приложения от сведений о конкретном устройстве, поэтому уровень приложения или системные вызовы могут быть общими или независимыми от устройства.

Драйверы доступны в следующих случаях:

Инициализация системы. Ядро вызывает драйверы устройств во время инициализации системы, чтобы определить, какие устройства доступны, и инициализировать эти устройства.

Системные вызовы из пользовательских процессов. Ядро вызывает драйвер устройства для выполнения операций ввода-вывода на устройстве, таких как open(2), read(2) и ioctl(2).

Запросы на уровне пользователя. Ядро вызывает драйверы устройств для обслуживания запросов от таких команд, как prtconf(1M).

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

Сброс шины. Ядро вызывает драйвер устройства для повторной инициализации драйвера, устройства или того и другого при сбросе шины. Шина — это путь от ЦП к устройству.

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

Рис. 1–3. Типичное взаимодействие драйвера устройства

Организация каталога водителей

Драйверы устройств и другие модули ядра организованы в следующие каталоги в ОС Oracle Solaris. Обратитесь к справочным страницам ядра(1M) и системы(4) для получения дополнительной информации об организации ядра и о том, как добавить каталоги в путь поиска модулей ядра.

Эти модули используются на большинстве платформ. Модули, необходимые для загрузки или инициализации системы, находятся в этом каталоге.

Эти модули зависят от платформы, определяемой командой uname -i.

Эти модули зависят от платформы, определяемой командой uname -m. Эти модули относятся к аппаратному классу, но более универсальны, чем модули в каталоге ядра uname -i.

Это пользовательские модули. Модули, которые не являются необходимыми для загрузки, находятся в этом каталоге. В этом руководстве показано, как поместить все ваши драйверы в каталог /usr/kernel.

Одно из преимуществ организации драйверов в разных каталогах заключается в том, что вы можете выборочно загружать разные группы драйверов при запуске, когда вы загружаетесь в интерактивном режиме по запросу загрузки, как показано в следующем примере. Для получения дополнительной информации см. справочную страницу boot(1M).

В этом примере расположение /usr/kernel исключено из списка каталогов для поиска загружаемых модулей. Вы можете сделать это, если у вас есть драйвер в /usr/kernel, который вызывает панику ядра во время запуска или при подключении. Вместо того, чтобы опускать все модули /usr/kernel, лучший способ тестирования драйверов — поместить их в отдельный каталог. Используйте переменную ядра moddir, чтобы добавить этот тестовый каталог в путь поиска модулей ядра. Переменная ядра moddir описана в kernel(1M) и system(4). Другой метод работы с драйверами, у которых могут возникать проблемы при запуске, описан в разделе Советы по тестированию драйверов устройств.

Модуль ядра – это программа, которая может быть загружена в ядро ​​или выгружена из него по требованию без обязательной перекомпиляции его (ядра) или перезагрузки системы и предназначена для улучшения функциональности ядра.

С точки зрения программного обеспечения модули более или менее похожи на подключаемые модули к такому программному обеспечению, как WordPress. Плагины предоставляют средства для расширения функциональности программного обеспечения, без них разработчикам пришлось бы создавать единое массивное программное обеспечение со всеми функциями, интегрированными в пакет. Если потребуются новые функции, их нужно будет добавить в новые версии программного обеспечения.

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

Простым примером модуля является драйвер устройства, который позволяет ядру получить доступ к аппаратному компоненту/устройству, подключенному к системе.

Список всех загруженных модулей ядра в Linux

В Linux все модули заканчиваются расширением .ko и обычно загружаются автоматически при обнаружении оборудования при загрузке системы. Однако системный администратор может управлять модулями с помощью определенных команд.

Чтобы вывести список всех загруженных в данный момент модулей в Linux, мы можем использовать команду lsmod (list modules), которая читает содержимое /proc/modules следующим образом.

Как загрузить и выгрузить (удалить) модули ядра в Linux

Чтобы загрузить модуль ядра, мы можем использовать команду insmod (вставить модуль). Здесь мы должны указать полный путь к модулю. Приведенная ниже команда вставит модуль speedstep-lib.ko.

Чтобы выгрузить модуль ядра, мы используем команду rmmod (удалить модуль). В следующем примере будет выгружен или удален модуль speedstep-lib.ko.

Как управлять модулями ядра с помощью команды modprobe

modprobe — это интеллектуальная команда для просмотра, вставки и удаления модулей из ядра. Он ищет в каталоге модулей /lib/modules/$(uname -r) все модули и связанные файлы, но исключает альтернативные файлы конфигурации в каталоге /etc/modprobe.d.

Здесь вам не нужен абсолютный путь к модулю; это преимущество использования modprobe по сравнению с предыдущими командами.

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

Чтобы удалить модуль, используйте флаг -r следующим образом.

Примечание: в modprobe выполняется автоматическое преобразование подчеркивания, поэтому нет никакой разницы между _ и - при вводе имен модулей.

Для получения дополнительной информации об использовании и параметрах прочтите справочную страницу modprobe.

Не забудьте проверить:

На этом пока все! Если у вас есть какие-либо полезные идеи, которые вы хотели бы, чтобы мы добавили в это руководство, или вопросы, используйте форму обратной связи ниже, чтобы отправить их нам.

Если вам понравилась эта статья, подпишитесь на уведомления по электронной почте о руководствах по Linux. Если у вас есть вопросы или сомнения? обратитесь за помощью в разделе комментариев.

Если вы цените то, что мы делаем здесь, в TecMint, вам следует подумать о следующем:

TecMint – это самый быстрорастущий и пользующийся наибольшим доверием сайт сообщества, где можно найти любые статьи, руководства и книги по Linux в Интернете. Миллионы людей посещают TecMint! для поиска или просмотра тысяч опубликованных статей, доступных всем БЕСПЛАТНО.

Если вам нравится то, что вы читаете, купите нам кофе (или 2) в знак признательности.

Поддержите нас

Мы благодарны за вашу бесконечную поддержку.

Похожие записи

Среды развертывания программного обеспечения

Основы контейнеров

Основы DevOps

Безопасность системы Linux

Сеть Linux Советы по безопасности

Безопасность Linux Советы по укреплению

6 мыслей о «Как загружать и выгружать модули ядра в Linux»

Отличная статья. Я удалил много модулей. Моя система Linux теперь работает быстрее. спасибо!

Что произойдет, если мы попытаемся загрузить уже загруженный модуль?

Есть ли в этом недостатки? Как команда работает внутри?

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

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

Привет! Отличный пост.
Можно ли получить описание различных модулей? Существуют ли справочные страницы для них?

Спасибо, надеюсь, вам понравилось. Чтобы просмотреть информацию о модуле, используйте команду modinfo следующим образом:
modinfo speedstep-lib

Есть что сказать? Присоединяйтесь к обсуждению. Отменить ответ

Этот сайт использует Akismet для уменьшения количества спама. Узнайте, как обрабатываются данные ваших комментариев.


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

Linux — это монолитное ядро; то есть это одна большая программа, в которой все функциональные компоненты ядра имеют доступ ко всем его внутренним структурам данных и процедурам.Альтернативой является структура микроядра, в которой функциональные части ядра разбиты на отдельные блоки со строгими механизмами связи между ними. Это делает добавление новых компонентов в ядро ​​через процесс настройки довольно трудоемким. Допустим, вы хотели использовать драйвер SCSI для NCR 810 SCSI, но не встроили его в ядро. Вам придется настроить, а затем собрать новое ядро, прежде чем вы сможете использовать NCR 810. Существует альтернатива, Linux позволяет вам динамически загружать и выгружать компоненты операционной системы по мере необходимости. Модули Linux представляют собой куски кода, которые могут быть динамически связаны с ядром в любой момент после загрузки системы. Их можно отвязать от ядра и удалить, когда они больше не нужны. В основном модули ядра Linux представляют собой драйверы устройств, драйверы псевдоустройств, такие как сетевые драйверы или файловые системы.

Вы можете либо загружать и выгружать модули ядра Linux явно с помощью команд insmod и rmmod, либо само ядро ​​может потребовать, чтобы демон ядра (kerneld) загружал и выгружал модули по мере необходимости.< /p>

Динамическая загрузка кода по мере необходимости привлекательна, так как позволяет свести к минимуму размер ядра и делает его очень гибким. Мое текущее ядро ​​​​Intel широко использует модули и имеет длину всего 406 Кбайт. Я лишь изредка использую файловые системы VFAT, поэтому я создаю ядро ​​Linux для автоматической загрузки модуля файловой системы VFAT при монтировании раздела VFAT. Когда я размонтировал раздел VFAT, система обнаруживает, что мне больше не нужен модуль файловой системы VFAT, и удаляет его из системы. Модули также могут быть полезны для опробования нового кода ядра без необходимости пересборки и перезагрузки ядра каждый раз, когда вы его пробуете. Ничто, однако, не является бесплатным, и есть небольшая потеря производительности и памяти, связанная с модулями ядра. Загружаемый модуль должен предоставить немного больше кода, и эта и дополнительные структуры данных занимают немного больше памяти. Также введен уровень косвенности, который делает доступ к ресурсам ядра немного менее эффективным для модулей.

После загрузки модуль Linux становится такой же частью ядра, как и любой нормальный код ядра. Он имеет те же права и обязанности, что и любой код ядра; другими словами, модули ядра Linux могут вызвать сбой ядра точно так же, как и любой код ядра или драйверы устройств.

Чтобы модули могли использовать необходимые им ресурсы ядра, они должны уметь их находить. Скажем, модуль должен вызвать kmalloc(), процедуру выделения памяти ядра. Во время сборки модуль не знает, где в памяти находится kmalloc(), поэтому при загрузке модуля ядро ​​должно исправить все ссылки модуля на kmalloc. () до того, как модуль сможет работать. Ядро хранит список всех ресурсов ядра в таблице символов ядра, чтобы оно могло разрешать ссылки на эти ресурсы из модулей по мере их загрузки. Linux допускает стекирование модулей, когда одному модулю требуются услуги другого модуля. Например, модуль файловой системы VFAT требует служб модуля файловой системы FAT, поскольку файловая система VFAT более или менее представляет собой набор расширения файловой системы FAT. Один модуль требует услуг или ресурсов от другого модуля, очень похоже на ситуацию, когда модуль требует услуг и ресурсов от самого ядра. Только вот нужные сервисы находятся в другом, ранее загруженном модуле. При загрузке каждого модуля ядро ​​модифицирует таблицу символов ядра, добавляя в нее все ресурсы или символы, экспортированные вновь загруженным модулем. Это означает, что при загрузке следующего модуля он имеет доступ к службам уже загруженных модулей.

При попытке выгрузить модуль ядру необходимо знать, что модуль не используется, и ему нужен какой-то способ уведомить модуль о том, что он будет выгружен. Таким образом, модуль сможет освободить любые выделенные им системные ресурсы, например, память ядра или прерывания, прежде чем он будет удален из ядра. Когда модуль выгружается, ядро ​​удаляет все символы, которые этот модуль экспортировал в таблицу символов ядра.

Помимо того, что загруженный модуль может привести к сбою операционной системы из-за того, что он плохо написан, он представляет еще одну опасность. Что произойдет, если вы загрузите модуль, созданный для ядра более ранней или более поздней версии, чем то, которое вы сейчас используете? Это может вызвать проблему, если, скажем, модуль вызывает подпрограмму ядра и предоставляет неправильные аргументы. Ядро может при желании защититься от этого, выполнив тщательную проверку версии модуля по мере его загрузки.

12.1 Загрузка модуля

Рисунок 12.1: Список модулей ядра

Есть два способа загрузки модуля ядра.Первый способ — использовать команду insmod, чтобы вручную вставить его в ядро.

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

Когда ядро ​​обнаруживает потребность в модуле, например, когда пользователь монтирует файловую систему, которой нет в ядре, ядро ​​​​запросит, чтобы демон ядра (kerneld) попытался загрузить соответствующий модуль.

Демон ядра — это обычный пользовательский процесс, хотя и с привилегиями суперпользователя. Когда он запускается, обычно во время загрузки системы, он открывает канал межпроцессного взаимодействия (IPC) с ядром. Эта ссылка используется ядром для отправки сообщений ядру с запросом на выполнение различных задач.

Основная функция

Kerneld заключается в загрузке и выгрузке модулей ядра, но он также способен выполнять другие задачи, такие как запуск канала PPP по последовательной линии, когда это необходимо, и его закрытие, когда это необходимо. не является. Kerneld не выполняет эти задачи самостоятельно, он запускает необходимые программы, такие как insmod, для выполнения этой работы. Kerneld — это просто агент ядра, планирующий работу от его имени.

Утилита insmod должна найти запрошенный модуль ядра, который она должна загрузить. Модули ядра, загружаемые по требованию, обычно хранятся в /lib/modules/kernel-version. Модули ядра представляют собой связанные объектные файлы, как и другие программы в системе, за исключением того, что они связаны как перемещаемые образы. То есть изображения, которые не связаны, запускаются с определенного адреса. Это могут быть объектные файлы форматов a.out или elf. insmod выполняет привилегированный системный вызов для поиска экспортированных символов ядра.

Они хранятся парами, содержащими имя символа и его значение, например, его адрес. Экспортируемая ядром таблица символов хранится в первой структуре данных module в списке модулей, поддерживаемых ядром, и на нее указывает указатель module_list.

В таблицу добавляются только специально введенные символы, которая строится при компиляции и линковке ядра, а не каждый символ в ядре экспортируется в его модули. Примером этого символа является ``request_irq'', представляющий собой подпрограмму ядра, которая должна быть вызвана, когда драйвер хочет взять под контроль определенное системное прерывание. В моем текущем ядре это имеет значение 0x0010cd30. Вы можете легко увидеть экспортированные символы ядра и их значения, заглянув в /proc/ksyms или воспользовавшись утилитой ksyms. Утилита ksyms может показать вам все экспортированные символы ядра или только те символы, которые экспортируются загруженными модулями. insmod считывает модуль в свою виртуальную память и исправляет его неразрешенные ссылки на подпрограммы и ресурсы ядра, используя экспортированные символы из ядра. Это исправление принимает форму исправления образа модуля в памяти. insmod физически записывает адрес символа в соответствующее место модуля.

Когда insmod исправит ссылки модуля на экспортированные символы ядра, он запрашивает у ядра достаточно места для хранения нового ядра, снова используя привилегированный системный вызов. Ядро выделяет новую структуру данных module и достаточный объем памяти ядра для хранения нового модуля и помещает его в конец списка модулей ядра. Новый модуль помечен как UNINITIALIZED.

На рис. 12.1 показан список модулей ядра после загрузки в ядро ​​двух модулей, VFAT и VFAT. На диаграмме не показан первый модуль в списке, который является псевдомодулем, предназначенным только для хранения экспортированной таблицы символов ядра. Вы можете использовать команду lsmod для получения списка всех загруженных модулей ядра и их взаимозависимостей. lsmod просто переформатирует /proc/modules, который создается из списка структур данных ядра module. Память, которую выделяет для него ядро, отображается в адресное пространство процесса insmod, чтобы он мог получить к ней доступ. insmod копирует модуль в выделенное пространство и перемещает его так, чтобы он запускался с выделенного адреса ядра. Это должно произойти, поскольку модуль не может быть загружен по одному и тому же адресу дважды, не говоря уже об одном и том же адресе в двух разных системах Linux. Опять же, это перемещение включает в себя исправление образа модуля с соответствующими адресами.

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

Когда в ядро ​​добавляется новый модуль, он должен обновить набор символов ядра и изменить модули, используемые новым модулем. Модули, от которых зависят другие модули, должны поддерживать список ссылок в конце своей таблицы символов, на который указывает их структура данных module. На рис. 12.1 показано, что модуль файловой системы VFAT зависит от модуля файловой системы FAT. Итак, модуль FAT содержит ссылку на модуль VFAT; ссылка была добавлена ​​при загрузке модуля VFAT. Ядро вызывает процедуру инициализации модулей и, в случае успеха, продолжает установку модуля. Адрес подпрограммы очистки модуля хранится в его структуре данных module и будет вызываться ядром при выгрузке этого модуля. Наконец, состояние модуля установлено на RUNNING.

12.2 Выгрузка модуля

Модули можно удалить с помощью команды rmmod, но загруженные по требованию модули автоматически удаляются из системы kerneld, когда они больше не используются. Каждый раз, когда истекает время простоя, kerneld выполняет системный вызов, запрашивающий удаление из системы всех неиспользуемых загруженных по запросу модулей. Значение таймера устанавливается при запуске kerneld; мое kerneld проверяет каждые 180 секунд. Так, например, если вы монтируете компакт-диск iso9660 и ваша файловая система iso9660 является загружаемым модулем, то вскоре после размонтирования компакт-диска iso9660< /tt> модуль будет удален из ядра.

Модуль нельзя выгрузить, пока от него зависят другие компоненты ядра. Например, вы не можете выгрузить модуль VFAT, если у вас смонтирована одна или несколько файловых систем VFAT. Если вы посмотрите на вывод lsmod, вы увидите, что у каждого модуля есть связанный с ним счетчик. Например:

Число — это количество объектов ядра, зависящих от этого модуля. В приведенном выше примере модули vfat и msdos зависят от модуля fat, поэтому его счетчик равен 2. Оба модуля < Модули tt>vfat и msdos имеют 1 зависимую, которая представляет собой смонтированную файловую систему. Если бы мне пришлось загрузить другую файловую систему VFAT, счетчик модуля vfat стал бы равным 2. Счетчик модуля хранится в первом длинном слове его образа.

Это поле немного перегружено, поскольку оно также содержит флаги AUTOCLEAN и VISITED. Оба этих флага используются для загружаемых по запросу модулей. Эти модули помечены как AUTOCLEAN, чтобы система могла распознать, какие из них она может автоматически выгрузить. Флаг VISITED помечает модуль как используемый одним или несколькими другими компонентами системы; он устанавливается всякий раз, когда другой компонент использует модуль. Каждый раз, когда kerneld просит систему удалить неиспользуемые загруженные по запросу модули, он просматривает все модули в системе в поисках вероятных кандидатов. Он проверяет только модули, помеченные как AUTOCLEAN и находящиеся в состоянии RUNNING. Если у кандидата снят флаг VISITED, он удалит модуль, в противном случае он очистит флаг VISITED и перейдет к просмотру следующего модуля в системе.< /p>

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

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

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

3.1. Введение в модули ядра

Ядро Red Hat Enterprise Linux может быть дополнено необязательными дополнительными функциями, называемыми модулями ядра, без перезагрузки системы. В Red Hat Enterprise Linux 8 модули ядра представляют собой дополнительный код ядра, встроенный в сжатые объектные файлы .ko.xz.

  • Драйвер устройства, добавляющий поддержку нового оборудования.
  • Поддержка файловой системы, такой как GFS2 или NFS
  • Системные вызовы

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

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

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

3.2. Введение в спецификацию загрузчика

Спецификация загрузчика (BLS) определяет схему и формат файла для управления конфигурацией загрузчика для каждого варианта загрузки в раскрывающемся каталоге без необходимости манипулировать файлами конфигурации загрузчика. В отличие от более ранних подходов, каждая загрузочная запись теперь представлена ​​отдельным файлом конфигурации в каталоге drop-in. Вставной каталог расширяет свою конфигурацию без необходимости редактирования или повторного создания файлов конфигурации. BLS расширяет эту концепцию для пунктов меню загрузки.

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

Инструмент Grubby представляет собой тонкий скрипт-оболочку для BLS и поддерживает те же аргументы и параметры Grubby. Он запускает dracut для создания начального образа виртуального диска. При такой настройке основные файлы конфигурации загрузчика являются статическими и не изменяются после установки ядра.

Эта предпосылка особенно актуальна для RHEL 8, поскольку один и тот же загрузчик используется не во всех архитектурах. GRUB2 используется в большинстве из них, таких как 64-разрядная версия ARM, но варианты IBM Power Systems с прямым порядком байтов с Open Power Abstraction Layer (OPAL) используют Petitboot, а архитектура IBM Z использует zipl .

Дополнительные ресурсы

3.3. Зависимости модуля ядра

Некоторые модули ядра иногда зависят от одного или нескольких других модулей ядра. Файл /lib/modules/ /modules.dep содержит полный список зависимостей модулей ядра для соответствующей версии ядра.

Файл зависимостей создается программой depmod, которая является частью пакета kmod. Многие утилиты, предоставляемые kmod, учитывают зависимости модулей при выполнении операций, поэтому ручное отслеживание зависимостей требуется редко.

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

Дополнительные ресурсы

  • страница руководства modules.dep(5)
  • страница руководства depmod(8)

3.4. Список загруженных в данный момент модулей ядра

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

Предпосылки

Процедура

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

В приведенном выше примере:

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

Дополнительные ресурсы

  • /usr/share/doc/kmod/файл README
  • страница руководства lsmod(8)

3.5. Список всех установленных ядер

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

Предпосылки

Процедура

Чтобы получить список всех установленных ядер, выполните:

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

Дополнительные ресурсы

3.6. Установка ядра по умолчанию

Следующая процедура описывает, как установить конкретное ядро ​​по умолчанию с помощью инструмента командной строки grubby и GRUB2 .

Процедура

Установка ядра по умолчанию с помощью инструмента grubby

  • Выполните следующую команду, чтобы установить ядро ​​по умолчанию с помощью инструмента grubby:

Команда использует идентификатор машины без суффикса .conf в качестве аргумента.

Идентификатор машины находится в каталоге /boot/loader/entries/.

  • Выведите список загрузочных записей, используя аргумент id, а затем установите нужное ядро ​​по умолчанию:
  • Выполните следующую команду, чтобы установить ядро ​​по умолчанию только для следующей перезагрузки с помощью команды grub2-reboot:

Осторожно установите ядро ​​по умолчанию только для следующей загрузки. Установка новых RPM ядер, самосборных ядер и добавление записей вручную в каталог /boot/loader/entries/ может привести к изменению значений индекса.

3.7. Отображение информации о модулях ядра

При работе с модулем ядра может потребоваться дополнительная информация об этом модуле. Эта процедура описывает, как отобразить дополнительную информацию о модулях ядра.

Предпосылки

Процедура

Чтобы отобразить информацию о любом модуле ядра, выполните:

Команда modinfo отображает некоторую подробную информацию об указанном модуле ядра. Вы можете запросить информацию обо всех доступных модулях, независимо от того, загружены они или нет. Записи parm показывают параметры, которые пользователь может установить для модуля, и тип ожидаемого значения.

При вводе имени модуля ядра не добавляйте .ko.xz в конец имени. Имена модулей ядра не имеют расширений; соответствующие им файлы.

Дополнительные ресурсы

3.8. Загрузка модулей ядра во время работы системы

Оптимальный способ расширить функциональность ядра Linux — загрузить модули ядра. Следующая процедура описывает, как использовать команду modprobe для поиска и загрузки модуля ядра в работающее в данный момент ядро.

Предпосылки

  • Корневые права
  • Пакет kmod установлен.
  • Соответствующий модуль ядра не загружен. Чтобы убедиться, что это так, перечислите загруженные модули ядра.

Процедура

Выберите модуль ядра, который хотите загрузить.

Модули расположены в каталоге /lib/modules/$(uname -r)/kernel/ /.

Загрузите соответствующий модуль ядра:

При вводе имени модуля ядра не добавляйте расширение .ko.xz в конец имени. Имена модулей ядра не имеют расширений; соответствующие им файлы.

При необходимости проверьте, загружен ли соответствующий модуль:

Если модуль был загружен правильно, эта команда отображает соответствующий модуль ядра. Например:

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

Дополнительные ресурсы

3.9. Выгрузка модулей ядра во время работы системы

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

Предпосылки

  • Корневые права
  • Пакет kmod установлен.

Процедура

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

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

Выгрузите соответствующий модуль ядра:

При вводе имени модуля ядра не добавляйте расширение .ko.xz в конец имени. Имена модулей ядра не имеют расширений; соответствующие им файлы.

Не выгружайте модули ядра, когда они используются работающей системой. Это может привести к нестабильности или неработоспособности системы.

При необходимости убедитесь, что соответствующий модуль был выгружен:

Если модуль был успешно выгружен, эта команда ничего не выводит.

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

Дополнительные ресурсы

3.10. Выгрузка модулей ядра на ранних этапах процесса загрузки

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

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

Предпосылки

  • У вас есть загружаемый модуль ядра, который вы хотите по какой-то причине не загружать.

Процедура

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

    Используйте клавиши курсора, чтобы выделить соответствующую запись загрузчика.

Нажмите клавишу e, чтобы изменить запись.

Рисунок 3.1. Меню загрузки ядра

Добавить modprobe.blacklist= module_name в конец строки.

Рисунок 3.2. Загрузочная запись ядра

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

Подтверждение

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

Дополнительные ресурсы

3.11. Автоматическая загрузка модулей ядра во время загрузки системы

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

Предпосылки

  • Корневые права
  • Пакет kmod установлен.

Процедура

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

Модули расположены в каталоге /lib/modules/$(uname -r)/kernel/ /.

Создайте файл конфигурации для модуля:

При вводе имени модуля ядра не добавляйте расширение .ko.xz в конец имени. Имена модулей ядра не имеют расширений; соответствующие им файлы.

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

Приведенный выше пример команды должен завершиться успешно и отобразить соответствующий модуль ядра.

Изменения, описанные в этой процедуре, сохранятся после перезагрузки системы.

Дополнительные ресурсы

3.12. Предотвращение автоматической загрузки модулей ядра во время загрузки системы

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

Предпосылки

  • Корневые права
  • Пакет kmod установлен.
  • Убедитесь, что модуль ядра в списке запрещенных не является жизненно важным для вашей текущей конфигурации системы.

Процедура

Выберите модуль ядра, который вы хотите поместить в список запрещенных:

Команда lsmod отображает список модулей, загруженных в работающее в данный момент ядро.

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

Все модули ядра расположены в каталоге /lib/modules/ /kernel/ /.

Создайте файл конфигурации для черного списка:

В примере показано содержимое файла blacklist.conf, отредактированное редактором vim. Строка черного списка гарантирует, что соответствующий модуль ядра не будет автоматически загружен в процессе загрузки. Однако команда blacklist не предотвращает загрузку модуля в качестве зависимости для другого модуля ядра, которого нет в списке запрещенных. Поэтому строка установки вызывает запуск /bin/false вместо установки модуля.

Строки, начинающиеся со знака решетки, являются комментариями, чтобы сделать файл более читабельным.

При вводе имени модуля ядра не добавляйте расширение .ko.xz в конец имени. Имена модулей ядра не имеют расширений; соответствующие им файлы.

Создайте резервную копию текущего исходного образа виртуального диска перед перестроением:

Приведенная выше команда создает резервный образ initramfs на случай, если в новой версии возникнут непредвиденные проблемы.

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

Создайте новый начальный образ виртуального диска, чтобы отразить изменения:

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

Перезагрузите систему:

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

Дополнительные ресурсы

3.13. Компиляция пользовательских модулей ядра

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

Предпосылки

  • Вы установили пакеты kernel-devel , gcc и elfutils-libelf-devel.
  • У вас есть права root.
  • Вы создали каталог /root/testmodule/, в котором компилируете пользовательский модуль ядра.

Процедура

Создайте файл /root/testmodule/test.c со следующим содержимым:

Файл test.c — это исходный файл, обеспечивающий основные функции модуля ядра. Файл был создан в специальном каталоге /root/testmodule/ для организационных целей. После компиляции модуля каталог /root/testmodule/ будет содержать несколько файлов.

Файл test.c включает в себя из системных библиотек:

    Заголовочный файл linux/kernel.h необходим для функции printk() в примере кода.

Файл linux/module.h содержит объявления функций и определения макросов, которые должны совместно использоваться несколькими исходными файлами, написанными на языке программирования C.

Далее следуют функции init_module() и cleanup_module(), чтобы запустить и завершить функцию ведения журнала ядра printk() , которая печатает текст.

Читайте также: