Руководство по ядру linux

Перед вами последняя версия пособия по программированию модулей ядра Linux, вышедшего 2 июля 2022 года. Пособие большое, поэтому материал будет разбит на серию статей. В первой части мы разберём, что такое модули ядра, рассмотрим необходимые подготовительные этапы для их создания и в завершении по традиции напишем первый простейший модуль «Hello world», попутно разобрав вопросы лицензирования, передачу аргументов командной строки и прочие нюансы. Это пособие вы можете смело воспроизводить и изменять в соответствии с условиями Open Software License v 3.0.

▍ Готовые части руководства:

  • Вы тут —> Часть 1
  • Часть 2
  • Часть 3
  • Часть 4
  • Часть 5
  • Часть 6
  • Часть 7

1. Вступление

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

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

Производные работы и переводы этого документа должны также публиковаться под лицензией Open Software License с упоминанием оригинального источника. Если вы внесёте в книгу новый материал, то сделайте этот материал и исходный код доступными для своих ревизий. Ревизии и обновления должны быть доступны непосредственно мейнтейнеру документа, Джиму Хуангу <jserv@ccns.ncku.edu.tw>. Это позволит делать мерджи обновлений и предоставлять согласованные ревизии сообществу Linux.

Если вы будете публиковать или распространять книгу в коммерческих целях, то автор и проект документирования Linux (LDP) будут весьма признательны за пожертвования, ройялти-отчисления и предоставление печатных версий. Участие в общем деле таким образом показывает вашу поддержку бесплатного ПО и LDP. По всем вопросам можете писать на приведённый выше адрес.

▍ 1.1 Авторство

Первое «Руководство по программированию модулей ядра» написал Ори Померанц для ядер версии 2.2. В конечном итоге у Ори не стало времени для поддержания актуальности этого документа, что не удивительно, ведь ядро очень динамично в своём развитии. После этого поддержку руководства взял на себя Питер Джей Зальцман, который обновил его под версию 2.4. Но в итоге и Питеру стало нехватать времени, чтобы довести пособие до соответствия ядру 2.6. В этой ситуации на выручку пришёл Майкл Буриан, который помог его обновить. Следующим был Боб Моттрам, доработавший примеры под ядро 3.8+. Последним же на данный момент является Джим Хуанг, который довёл руководство до соответствия последним версиям ядра 5.х и скорректировал документ LaTeX.

▍ 1.2 Благодарности

Ниже перечислены сторонние участники, которые вносили изменения и давали полезные рекомендации:

2011eric, 25077667, Arush Sharma, asas1asas200, Benno Bielmeier, Bob Lee, Brad Baker, ccs100203, Chih-Yu Chen, Ching-Hua (Vivian) Lin, ChinYikMing, Cyril Brulebois, Daniele Paolo Scarpazza, David Porter, demonsome, Dimo Velev, Ekang Monyet, fennecJ, Francois Audeon, gagachang, Gilad Reti, Horst Schirmeier, Hsin-Hsiang Peng, Ignacio Martin, JianXing Wu, linD026, lyctw, manbing, Marconi Jiang, mengxinayan, RinHizakura, Roman Lakeev, Stacy Prowell, Steven Lung, Tristan Lelong, Tucker Polomik, VxTeemo, Wei-Lun Tsai, xatier, Ylowy.

▍ 1.3 Что такое модуль ядра?

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

Что же конкретно такое модуль ядра? Модули – это элементы кода, которые по необходимости можно загружать в ядро и выгружать. Они расширяют его функциональность, не требуя перезагрузки системы. К примеру, одним из типов модулей является драйвер устройств, который позволяет ядру обращаться к подключённому аппаратному обеспечению. Не имея модулей, нам бы пришлось строить монолитные ядра и добавлять новую функциональность непосредственно в их образы. И мало того что это привело бы к увеличению размеров ядра, но ещё и вынудило бы нас пересобирать и перезагружать его при каждом добавлении новой функциональности.

▍ 1.4 Пакеты модулей ядра

В дистрибутивах Linux для работы с пакетами модулей есть команды modprobe, insmod и depmod.

В Ubuntu/Debian:

sudo apt-get install build-essential kmod

В Arch Linux:

sudo pacman -S gcc kmod

▍ 1.5 Какие модули содержатся в моём ядре?

Узнать, какие модули загружены в ядро, можно командой lsmod:

sudo lsmod

Хранятся модули по пути /proc/modules, значит, их также можно просмотреть с помощью:

sudo cat /proc/modules

Список может оказаться длинным, и вам потребуется искать что-то конкретное. Вот пример поиска модуля fat:

sudo lsmod | grep fat

▍ 1.6 Нужно ли скачивать и компилировать ядро?

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

▍ 1.7 Перед началом

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

  1. Версионирование модулей. Модуль, скомпилированный для одного ядра, не загрузится для другого, если не включить в этом ядре CONFIG_MODVERSIONS. Подробнее о версионировании мы ещё поговорим позднее. Если в вашем ядре версионирование включено, то поначалу, пока мы не разберём эту тему подробнее, примеры могут у вас не работать. Правда, включено оно обычно в большинстве базовых дистрибутивов, и если из-за этого у вас возникнут проблемы с загрузкой модулей, скомпилируйте ядро, отключив их версионирование.
  2. Использование X Window System. Настоятельно рекомендуем извлекать, компилировать и загружать все приводимые в руководстве примеры из консоли. Работать с ними в X Window System не стоит.

Модули не могут выводить информацию на экран подобно printf(). При этом они могут логировать информацию и предупреждения, которые в итоге на экран выводятся, но только в консоли. Если вы вставите модуль (insmod) из xterm, то информация и предупреждения залогируются, но только в системный журнал. То есть увидеть вы все эти данные сможете лишь через journalctl. Подробности описаны в разделе 4. Для получения прямого доступа ко всей этой информации, выполняйте все действия в консоли.

2. Заголовочные файлы

Прежде чем вы сможете что-либо создавать, вам нужно установить для ядра заголовочные файлы. Для начала выполните:

в Ubuntu/Debian:

sudo apt-get update
apt-cache search linux-headers-`uname -r`

в Arch Linux:

sudo pacman -S linux-headers

Так вы узнаете, какие заголовочные файлы ядра доступны. Затем можно выполнить, например:

sudo apt-get install kmod linux-headers-5.4.0-80-generic

3. Примеры

Все примеры этого документа доступны в подкаталоге examples.

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

4. Hello World

▍ 4.1 Простейший модуль

Большинство людей, изучающих программирование, начинают с какого-нибудь примера «Hello world». Не знаю, что бывает с теми, кто от этой традиции отходит, но, думаю, лучше и не знать. Мы начнём с серии программ «Hello world», которые продемонстрируют различные основы написания модуля ядра.

Ниже описан простейший пример модуля.

Создайте тестовый каталог:

mkdir -p ~/develop/kernel/hello-1
cd ~/develop/kernel/hello-1

Вставьте следующий код в редактор и сохраните как hello-1.c:

/*
 * hello-1.c – простейший модуль ядра.
 */
#include <linux/kernel.h> /* необходим для pr_info() */
#include <linux/module.h> /* необходим для всех модулей */
 
int init_module(void)
{
    pr_info("Hello world 1.n");
 
    /* Если вернётся не 0, значит, init_module провалилась; модули загрузить не получится. */
    return 0;
}
 
void cleanup_module(void)
{
    pr_info("Goodbye world 1.n");
}
 
MODULE_LICENSE("GPL");

Теперь вам потребуется Makefile. Если вы будете копировать следующий код, то сделайте отступы табами, не пробелами.

obj-m += hello-1.o
 
PWD := $(CURDIR)
 
all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
 
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

В Makefile инструкция $(CURDIR) может быть установлена на абсолютный путь текущего рабочего каталога (затем идёт обработка всех опций , если таковые присутствуют). Подробнее о CURDIR читайте в мануале GNU make.

В завершении просто выполните make.

make

Если в Makefile не будет инструкции PWD := $(CURDIR), он может не скомпилироваться корректно с помощью sudo make. Поскольку некоторые переменные среды регулируются политикой безопасности, наследоваться они не могут. По умолчанию эта политика определяется файлом sudoers. В нём изначально включена опция env_reset, которая запрещает переменные среды. В частности, переменные PATH из пользовательской среды не сохраняются, а устанавливаются на значения по умолчанию (подробнее можно почитать в мануале по sudoers).

Установки для переменных среды можно посмотреть так:

$ sudo -s
# sudo -V

Вот пример простого Makefile, демонстрирующий описанную выше проблему:

all:
    echo $(PWD)

Далее можно использовать флаг –p для вывода всех значений переменных среды из Makefile.

$ make -p | grep PWD
PWD = /home/ubuntu/temp
OLDPWD = /home/ubuntu
    echo $(PWD)

Переменная PWD при выполнении sudo унаследована не будет.

$ sudo make -p | grep PWD
    echo $(PWD)

Тем не менее эту проблему можно решить тремя способами.

1. Использовать флаг -E для их временного сохранения.

$ sudo -E make -p | grep PWD
    PWD = /home/ubuntu/temp
    OLDPWD = /home/ubuntu
    echo $(PWD)

2. Отключить env_reset, отредактировав /etc/sudoers из-под рут-пользователя с помощью visudo.

## файл sudoers.
  ##
  ...
  Defaults env_reset
  ## В предыдущей строке измените env_reset на !env_reset, чтобы сохранить все переменные среды.

Затем выполните env и sudo env по отдельности:

# отключить env_reset
    echo "user:" > non-env_reset.log; env >> non-env_reset.log
    echo "root:" >> non-env_reset.log; sudo env >> non-env_reset.log
    # включить env_reset
    echo "user:" > env_reset.log; env >> env_reset.log
    echo "root:" >> env_reset.log; sudo env >> env_reset.log

Можете просмотреть и сравнить эти логи, чтобы понять отличия между env_reset и !env_reset.

3. Сохранить переменные среды, добавив их в env_keep в /etc/sudoers.

  Defaults env_keep += "PWD"

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

         $ sudo -s
         # sudo -V

Если всё пройдёт гладко, вы получите скомпилированный модуль hello-1.ko. Информацию о нём можно вывести командой:

modinfo hello-1.ko

На этом этапе команда:

sudo lsmod | grep hello

не должна ничего возвращать. Можете попробовать загрузить свой новоиспечённый модуль с помощью:

sudo insmod hello-1.ko

При этом символ тире превратится в нижнее подчёркивание. Теперь, когда вы снова выполните:

sudo lsmod | grep hello

то увидите загруженный модуль. Удалить его можно с помощью:

sudo rmmod hello_1

Обратите внимание — тире было заменено нижним подчёркиванием. Чтобы увидеть произошедшее в логах, выполните:

sudo journalctl --since "1 hour ago" | grep kernel

Теперь вам известны основы создания, компиляции, установки и удаления модулей. Далее мы подробнее разберём, как они работают.

Модули ядра должны иметь не менее двух функций:

  • стартовую (инициализация), которая называется init_module() и вызывается при внедрении (insmod) модуля в ядро;
  • завершающую (очистка), которая зовётся cleanup_module() и вызывается непосредственно перед извлечением модуля из ядра.

В действительности же с версии 2.3.13 произошли кое-какие изменения. Теперь стартовую и завершающую функцию модулей можно называть на своё усмотрение, и об этом будет подробнее сказано в разделе 4.2. На деле этот новый метод даже предпочтительней, хотя многие по прежнему используют названия init_module() и cleanup_module().

Как правило, init_module() или регистрирует обработчик чего-либо с помощью ядра, или заменяет одну из функций ядра собственным кодом (обычно кодом, который выполняет определённые действия и вызывает исходную функцию). Что касается cleanup_module(), то она должна отменять всё, что сделала init_module(), чтобы безопасно выгрузить модуль.

Наконец, каждый модуль ядра должен включать <linux/module.h>. Нам нужно было включить <linux/kernel.h> только для расширения макроса уровня журнала pr_alert(), о чём подробнее сказано в пункте 2.

  1. Примечание о стиле написания кода. Есть нюанс, который может не быть очевиден тем, кто только начинает заниматься программированием ядра. Имеется в виду то, что отступы в коде должны делаться с помощью табов, а не пробелов. Это одно из общих соглашений. Оно вам может не нравиться, но придётся привыкать, если вы соберётесь отправлять патч в основную ветку ядра.
  2. Добавление макросов вывода. Изначально использовалась функция printk, обычно сопровождаемая приоритетом уровня журнала KERN_DEBUG или KERN_INFO. Недавно же появилась возможность выражать это в сокращённой форме с помощью макросов вывода pr_info и pr_debug. Такой подход просто избавляет от лишних нажатий клавиш и выглядит более лаконично. Найти эти макросы можно в include/linux/printk.h. Рекомендую уделить время и прочесть о доступных макросах приоритетов.
  3. Насчёт компиляции. Модули ядра нужно компилировать несколько иначе, нежели обычные приложения пространства пользователя. Прежние версии ядра требовали от нас особого внимания к этим настройкам, которые обычно хранились в Makefile. И несмотря на иерархическую организованность, в make-файлах нижнего уровня скапливалось множество лишних настроек, что делало эти файлы большими и усложняло их обслуживание. К счастью, появился новый способ делать всё это, который называется kbuild, и процесс сборки для внешних загружаемых модулей теперь полностью интегрирован в стандартный механизм сборки ядра. Подробнее о компиляции модулей, не являющихся частью официального ядра (таких как примеры в этом руководства), читайте в Documentation/kbuild/modules.rst.

Дополнительные подробности о make-файлах для модулей ядра доступны в Documentation/kbuild/makefiles.rst. Обязательно прочтите эту документацию и изучите связанные с ней файлы – это наверняка избавит вас от большого объёма лишней работы.

А вот вам одно бонусное упражнение. Видите комментарий над инструкцией return в init_module()? Измените возвращаемое значение на отрицательное, после чего перекомпилируйте и заново загрузите модуль. Что произойдёт?

▍ 4.2 Hello и Goodbye

В ранних версиях ядра вам нужно было использовать функции init_module и cleanup_module, как в нашем первом примере «Hello world», но сегодня их уже можно именовать на своё усмотрение с помощью макросов module_init и module_exit, которые определены в include/linux/module.h. Единственное требование – это чтобы функции инициализации и очистки были определены до вызова этих макросов, в противном случае возникнут ошибки компиляции.

Вот пример:

/*
 * hello-2.c – демонстрация макросов module_init() и module_exit().
 * Этот вариант предпочтительнее использования init_module() и cleanup_module().
 */
#include <linux/init.h> /* Необходим для макросов */
#include <linux/kernel.h> /* Необходим для pr_info() */
#include <linux/module.h> /* Необходим всем модулям */
 
static int __init hello_2_init(void)
{
    pr_info("Hello, world 2n");
    return 0;
}
 
static void __exit hello_2_exit(void)
{
    pr_info("Goodbye, world 2n");
}
 
module_init(hello_2_init);
module_exit(hello_2_exit);
 
MODULE_LICENSE("GPL");

Теперь у нас есть уже два реальных модуля ядра. Добавить ещё один будет совсем несложно:

obj-m += hello-1.o
obj-m += hello-2.o
 
PWD := $(CURDIR)
 
all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
 
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Загляните в drivers/char/Makefile, чтобы увидеть реальный пример. Как видите, некоторые элементы включаются в ядро жёстко (obj-y), но куда делись все obj-m? Те, кто знаком со скриптами оболочки, смогут без проблем их обнаружить. Для остальных подскажу, что записи obj-$(CONFIG_FOO), которые вы видите повсюду, расширяются на obj-y или obj-m в зависимости от того, на какое значение была установлена переменная CONFIG_FOOy или m. Попутно отмечу, что именно эти переменные вы установили в файле .config в каталоге верхнего уровня дерева исходного кода в последний раз, когда выполнили make menuconfig или что-то в том духе.

▍ 4.3 Макросы __init и __exit

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

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

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

Эти макросы определены в include/linux/init.h и используются для освобождения памяти ядра. Если при его загрузке вы видите сообщение вроде Freeing unused kernel memory: 236k freed, то знайте – это тот самый процесс.

/*
 * hello-3.c – демонстрация макросов __init, __initdata и __exit.
 */
#include <linux/init.h> /* Необходим для макросов */
#include <linux/kernel.h> /* Необходим для pr_info() */
#include <linux/module.h> /* Необходим для всех модулей */
 
static int hello3_data __initdata = 3;
 
static int __init hello_3_init(void)
{
    pr_info("Hello, world %dn", hello3_data);
    return 0;
}
 
static void __exit hello_3_exit(void)
{
    pr_info("Goodbye, world 3n");
}
 
module_init(hello_3_init);
module_exit(hello_3_exit);
 
MODULE_LICENSE("GPL");

▍ 4.4 Лицензирование и документирование модулей

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

$ sudo insmod xxxxxx.ko
loading out-of-tree module taints kernel.
module license 'unspecified' taints kernel.

Для обозначения лицензии вашего модуля вы можете использовать ряд макросов, например: «GPL», «GPL v2», «GPL and additional rights», «Dual BSD/GPL», «Dual MIT/GPL», «Dual MPL/GPL» и «Proprietary». Определены они в include/linux/module.h.

Для указания используемой лицензии существует макрос MODULE_LICENSE. Он и ещё пара макросов, описывающих модуль, приведены в примере ниже.

/*
 * hello-4.c – Демонстрирует документирование модуля.
 */
#include <linux/init.h> /* Необходим для макросов */
#include <linux/kernel.h> /* Необходим для pr_info() */
#include <linux/module.h> /* Необходим для всех модулей */
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LKMPG");
MODULE_DESCRIPTION("A sample driver");
 
static int __init init_hello_4(void)
{
    pr_info("Hello, world 4n");
    return 0;
}
 
static void __exit cleanup_hello_4(void)
{
    pr_info("Goodbye, world 4n");
}
 
module_init(init_hello_4);
module_exit(cleanup_hello_4);

▍ 4.5 Передача в модуль аргументов командной строки

Модулям можно передавать аргументы командной строки, но не через argc/argv, к которым вы, возможно, привыкли.

Чтобы получить такую возможность, нужно объявить переменные, которые будут принимать значения аргументов командной строки как глобальные и затем использовать макрос module_param() (определяемый в include/linux/moduleparam.h) для настройки этого механизма. Во время выполнения insmod будет заполнять эти переменные получаемыми аргументами, например, insmod mymodule.ko myvariable=5. Для большей ясности объявления переменных и макросов необходимо размещать в начале модулей. Более наглядно всё это продемонстрировано в примере кода.

Макрос module_param() получает 3 аргумента: имя переменной, её тип и разрешения для соответствующего файла в sysfs. Целочисленные типы могут быть знаковыми, как обычно, или беззнаковыми. Если вы хотите использовать массивы целых чисел или строк, к вашим услугам module_param_array() и module_param_string().

int myint = 3;
module_param(myint, int, 0);

Массивы тоже поддерживаются, но в современных версиях работает это несколько иначе, нежели раньше. Для отслеживания количества параметров необходимо передать указатель на число переменных в качестве третьего аргумента. При желании вы можете вообще проигнорировать подсчёт и передать NULL. Вот пример обоих вариантов:

int myintarray[2];
module_param_array(myintarray, int, NULL, 0); /* если подсчёт не интересует */
 
short myshortarray[4];
int count;
module_param_array(myshortarray, short, &count, 0); /* подсчёт происходит в переменной "count" */

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

Наконец, есть ещё макрос MODULE_PARM_DESC(), используемый для документирования аргументов, которые может принять модуль. Он получает два параметра: имя переменной и строку в свободной форме, эту переменную описывающую.

Пример передачи аргументов командной строки в модуль

/*
 * hello-5.c – демонстрирует передачу аргументов командной строки в модуль.
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
 
MODULE_LICENSE("GPL");
 
static short int myshort = 1;
static int myint = 420;
static long int mylong = 9999;
static char *mystring = "blah";
static int myintarray[2] = { 420, 420 };
static int arr_argc = 0;
 
/* module_param(foo, int, 0000)
 * Первым аргументом указывается имя параметра.
 * Вторым указывается его тип.
 * Третьим указываются биты разрешений
 * для представления параметров в sysfs (если не нуль) позднее.
 */
module_param(myshort, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(myshort, "A short integer");
module_param(myint, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(myint, "An integer");
module_param(mylong, long, S_IRUSR);
MODULE_PARM_DESC(mylong, "A long integer");
module_param(mystring, charp, 0000);
MODULE_PARM_DESC(mystring, "A character string");
 
/* module_param_array(name, type, num, perm);
 * Первым аргументом идёт имя параметра (в данном случае массива).
 * Второй аргумент – это тип элементов массива.
 * Третий – это указатель на переменную, которая будет хранить количество элементов массива, инициализированных пользователем при загрузке модуля.
 * Четвёртый аргумент – это биты разрешения.
 */
module_param_array(myintarray, int, &arr_argc, 0000);
MODULE_PARM_DESC(myintarray, "An array of integers");
 
static int __init hello_5_init(void)
{
    int i;
 
    pr_info("Hello, world 5n=============n");
    pr_info("myshort is a short integer: %hdn", myshort);
    pr_info("myint is an integer: %dn", myint);
    pr_info("mylong is a long integer: %ldn", mylong);
    pr_info("mystring is a string: %sn", mystring);
 
    for (i = 0; i < ARRAY_SIZE(myintarray); i++)
        pr_info("myintarray[%d] = %dn", i, myintarray[i]);
 
    pr_info("got %d arguments for myintarray.n", arr_argc);
    return 0;
}
 
static void __exit hello_5_exit(void)
{
    pr_info("Goodbye, world 5n");
}
 
module_init(hello_5_init);
module_exit(hello_5_exit);

Рекомендую поэкспериментировать со следующим кодом:

$ sudo insmod hello-5.ko mystring="bebop" myintarray=-1
$ sudo dmesg -t | tail -7
myshort is a short integer: 1
myint is an integer: 420
mylong is a long integer: 9999
mystring is a string: bebop
myintarray[0] = -1
myintarray[1] = 420
got 1 arguments for myintarray.

$ sudo rmmod hello-5
$ sudo dmesg -t | tail -1
Goodbye, world 5

$ sudo insmod hello-5.ko mystring="supercalifragilisticexpialidocious" myintarray=-1,-1
$ sudo dmesg -t | tail -7
myshort is a short integer: 1
myint is an integer: 420
mylong is a long integer: 9999
mystring is a string: supercalifragilisticexpialidocious
myintarray[0] = -1
myintarray[1] = -1
got 2 arguments for myintarray.

$ sudo rmmod hello-5
$ sudo dmesg -t | tail -1
Goodbye, world 5

$ sudo insmod hello-5.ko mylong=hello
insmod: ERROR: could not insert module hello-5.ko: Invalid parameters

▍ 4.6 Модули, состоящие из нескольких файлов

Иногда есть смысл поделить модуль на несколько файлов.

Вот пример такого модуля:

/*
 * start.c – пример модулей, состоящих из нескольких файлов.
 */
 
#include <linux/kernel.h> /* Выполнение работы ядра. */
#include <linux/module.h> /* В частности, модуля. */
 
int init_module(void)
{
    pr_info("Hello, world - this is the kernel speakingn");
    return 0;
}
 
MODULE_LICENSE("GPL");

Второй файл:

/*
 * stop.c – пример модулей, состоящих из нескольких файлов.
 */
 
#include <linux/kernel.h> /* Выполнение работы ядра. */
#include <linux/module.h> /* В частности, модуля. */
 
void cleanup_module(void)
{
    pr_info("Short is the life of a kernel modulen");
}
 
MODULE_LICENSE("GPL");

И, наконец, Makefile:

obj-m += hello-1.o
obj-m += hello-2.o
obj-m += hello-3.o
obj-m += hello-4.o
obj-m += hello-5.o
obj-m += startstop.o
startstop-objs := start.o stop.o
 
PWD := $(CURDIR)
 
all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
 
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Это полный Makefile для всех примеров, которые мы успели рассмотреть. Первые пять строчек не представляют ничего особенного, но для последнего примера нам потребуется две строки. В первой мы придумываем имя объекта для нашего комбинированного модуля, а во второй сообщаем make, какие объектные файлы являются его частью.

▍ 4.7 Сборка модулей для скомпилированного ядра

Естественно, мы настоятельно рекомендуем вам перекомпилировать ядро, чтобы иметь возможность активировать ряд полезных функций отладки, таких как принудительная выгрузка модулей ( MODULE_FORCE_UNLOAD ): когда эта опция включена, можно с помощью команды sudo rmmod -f module принудить ядро выгрузить модуль, даже если оно сочтёт это небезопасным. В процессе разработки модуля эта опция может сэкономить вам много времени и избавить от лишних перезагрузок. Если вы не хотите перекомпилировать ядро, то рассмотрите вариант выполнения примеров внутри тестового дистрибутива на виртуальной машине. В таком случае при нарушении работоспособности вы сможете легко перезагрузиться или восстановить VM.

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

Итак, если вы просто установите дерево исходного кода, используете его для компиляции модуля и попытаетесь внедрить этот модуль в ядро, то в большинстве случаев получите ошибку:

insmod: ERROR: could not insert module poet.ko: Invalid module format

Более понятная информация логируется в системный журнал:

kernel: poet: disagrees about version of symbol module_layout

Иными словами, ваше ядро отказывается принимать модуль, потому что строки версии (точнее, vermagic, см. include/linux/vermagic.h) не совпадают. К слову, строки версии хранятся в объекте модуля в виде статической строки, начинающейся с vermagic:. Данные версии вставляются в модуль, когда он линкуется с файлом kernel/module.o. Для просмотра сигнатуры версии и прочих строк, хранящихся в конкретном модуле, выполните команду modinfo module.ko:

$ modinfo hello-4.ko
description:    A sample driver
author:         LKMPG
license:        GPL
srcversion:     B2AA7FBFCC2C39AED665382
depends:
retpoline:      Y
name:           hello_4
vermagic:       5.4.0-70-generic SMP mod_unload modversions

Для преодоления этой проблемы можно задействовать опцию --force-vermagic, но такое решение не гарантирует безопасность и однозначно будет неприемлемым в создании модулей. Следовательно, модуль нужно скомпилировать в среде, которая была идентична той, где создано наше скомпилированное ядро. Этому и будет посвящён остаток текущей главы.

Во-первых, убедитесь, что дерево исходного кода ядра вам доступно и имеет одинаковую версию с вашим текущим ядром. Далее найдите файл конфигурации, который использовался для компиляции ядра. Обычно он доступен в текущем каталоге boot под именем вроде config-5.14.x. Его будет достаточно скопировать в дерево исходного кода вашего ядра:

cp /boot/config-`uname -r` .config

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

Это небольшое отличие, а именно пользовательская строка, которая присутствует в версии модуля, но отсутствует в версии ядра, вызвано изменением относительно оригинала в Makefile, который содержат некоторые дистрибутивы. Далее вам нужно просмотреть собственный Makefile и обеспечить, чтобы представленная информация версии в точности соответствовала той, что указана в текущем ядре. Например, ваш Makefile может начинаться так:

VERSION = 5
PATCHLEVEL = 14
SUBLEVEL = 0
EXTRAVERSION = -rc2

В этом случае необходимо восстановить значение символа EXTRAVERSION на -rc2. Мы рекомендуем держать резервную копию Makefile, используемого для компиляции ядра, в /lib/modules/5.14.0-rc2/build. Для этого будет достаточно выполнить:

cp /lib/modules/`uname -r`/build/Makefile linux-`uname -r`

Здесь linux-`uname -r` — это исходный код ядра, которое вы собираетесь собрать.

Теперь выполните make для обновления конфигурации вместе с заголовками версии и объектами:

$ make
  SYNC    include/config/auto.conf.cmd
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/confdata.o
  HOSTCC  scripts/kconfig/expr.o
  LEX     scripts/kconfig/lexer.lex.c
  YACC    scripts/kconfig/parser.tab.[ch]
  HOSTCC  scripts/kconfig/preprocess.o
  HOSTCC  scripts/kconfig/symbol.o
  HOSTCC  scripts/kconfig/util.o
  HOSTCC  scripts/kconfig/lexer.lex.o
  HOSTCC  scripts/kconfig/parser.tab.o
  HOSTLD  scripts/kconfig/conf

Если же вы не хотите фактически компилировать ядро, то можете прервать процесс сборки (CTRL-C) сразу же после строки SPLIT, поскольку в этот момент необходимые вам файлы уже готовы.

Теперь можно вернуться в каталог модуля и скомпилировать его: он будет собран в точном соответствии с настройками текущего ядра и загрузится в него без каких-либо ошибок.

Продолжение

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

▍ Готовые части руководства:

  • Вы тут —> Часть 1
  • Часть 2
  • Часть 3
  • Часть 4
  • Часть 5
  • Часть 6
  • Часть 7

Мощные VPS на SSD со скидками до 53%. Панель ISPmanager в подарок*.

Мы с вами частично знакомы с некоторым функционалом ядра – оно отвечает за time sharing, управление процессами, их приоритетами и т.п, также управление памятью – та же виртуальная память, swap и всё что с этим связано, ну и из недавнего – отвечает за проверку прав на файлы — каким пользователям к каким файлам есть доступ. Это далеко не всё, чем занимается ядро, что-то мы ещё будем разбирать по мере изучения, но пока давайте разберём, что из себя представляет ядро и что администратору с ним делать.

Для начала, ядро — это программа. В отличии от других программ, оно лежит в директории /boot и называется vmlinuz:

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

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

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

Как видите, ядро весит почти 8 мегабайт. На самом деле, буква z в слове vmlinuz говорит о том, что ядро сжато. То есть, фактически, оно весит чуть больше.

Возможно, вы знаете – ядро Linux используется везде: android смартфоны, коих больше 3 миллиардов, работают на Linux; огромное количество сетевого оборудования, серверов, всяких медиабоксов, умных телевизоров, холодильников, машин, да даже бортовые компьютеры Space X – всё это работает на Linux. Это огромное количество разнообразного оборудования, которое должно поддерживать ядро. Поэтому в разработке ядра участвуют тысячи крупнейших компаний и специалистов. И всё ради 8 мегабайтного файла? Конечно нет. В этом файле только основной функционал, необходимый для работы – работа с памятью, управление процессами и т.п. Когда же ядру нужен дополнительный функционал – допустим, чтобы работать с сетевым адаптером, видеокартой или другим оборудованием – ядро обращается к специальным файлам, называемым модулями. В модулях хранится код, необходимый для работы с оборудованием или программный функционал – допустим, драйвер для видеокарты или программа для шифрования. Обычно это происходит незаметно для пользователя – вы вставили флешку, а ядро загрузило модуль для работы с usb флешками, а также модуль для работы с файловой системой на этой флешке. На других операционных системах это может работать по другому – есть различные архитектуры ядер операционных систем. У Linux архитектура модульная – то есть какой-то функционал вынесен в модули и подгружается по необходимости. Также Linux называют монолитным – потому что всё что делает ядро происходит в рамках одной программы – а правильнее сказать – все части ядра работают в одном адресном пространстве. Помните, мы обсуждали, что это такое, когда говорили о процессах? Но так как у Linux-а огромный функционал, который бессмысленно держать одновременно в памяти – поэтому функционал вынесен в модули, благодаря чему ускоряется работа ядра.

Так вот, модули ядра хранятся в директории /lib/modules/:

где есть директории для каждой версии установленного ядра. Зайдём в директорию текущего ядра:

cd /lib/modules/$(uname -r)
ls

и посмотрим файл modules.alias:

Тут перечислено — для каких устройств какие модули грузить в ядро.

В отличии от Windows, где вы ставите систему, а потом доустанавливаются драйвера, в Linux большинство драйверов уже предустановлены в виде модулей. Это благодаря тому, что многие производители оборудования сотрудничают с разработчиками ядра и предоставляют открытый исходный код драйверов на оборудование. Но, естественно, далеко не все производители предоставляют исходный код своих драйверов, из-за чего что-то может не работать из коробки – зачастую, это касается драйверов на wi-fi. Иногда, допустим, в случае с драйверами на видеокарты Nvidia, находятся энтузиасты, которые с помощью реверс инжиниринга создают свободные драйвера – т.е. берут драйвер с закрытым исходным кодом, изучают его с помощью специальных программ и методик и стараются воссоздать этот драйвер. Для видеокарт Nvidia таким образом создан драйвер nouveau. Зачастую это работает – естественно без каких-либо гарантий, потому что драйвер написан не самим производителем. При этом сам производитель – тот же Nvidia, также предоставляет свой драйвер в виде модуля, но уже с закрытым исходным кодом, т.е. проприетарный, поэтому он не бывает включён в ядро по умолчанию, из-за чего нужно самому доустанавить этот модуль. К примеру, после установки Centos на Virtualbox, мы с вами установили гостевые дополнения Virtualbox вручную именно потому, что они не под лицензией GPL, хотя сам Virtualbox имеет открытый исходный код с лицензией GPL. Всё это к тому, что если вы поставили Linux и у вас что-то не работает, допустим, wifi, то, скорее всего, производитель wifi адаптера не открыл исходный код своих драйверов и вам придётся искать нужный драйвер на сайте производителя, либо гуглить. Однако, некоторые юзер-френдли дистрибутивы, допустим Ubuntu, делают это за вас – после того, как вы установите Ubuntu, система найдёт нужные проприетарные драйвера и предложит вам их установить, что удобно для новичков.

Возвращаясь к нашему файлу, во втором столбике у нас перечислены идентификаторы оборудования – так называемые hardware id. Возьмём для примера radeon — /radeon – это видеокарты от AMD. Чтобы было удобнее, откроем сайт devicehunt.com – где мы можем увидеть информацию о вендорах и оборудовании. Так вот, если ядро видит, что к pci шине подключено устройство, у которого vendor id – 1002, а device id – 99A4 – ядро знает, что для этого устройства нужен модуль с названием radeon. Тут могут быть ещё версии какого-то оборудования, классы и подклассы – но нам это сейчас не особо важно. Если вам интересно, что значат все эти обозначения, посмотрите по ссылке.

Сам этот файл не статичный, он генерируется от информации из самих модулей. Чтобы увидеть информацию о каком-нибудь модуле, можно использовать команду modinfo – допустим:

Тут мы видим расположение и имя файла, причём, у всех модулей расширение .ko, ну а .xz в конце означает, что модуль в сжатом виде. Также лицензия, автор, описание. И чуть ниже у нас alias-ы – собственно на основе этого и генерируется файл modules.alias, который мы смотрели. Ну и ниже – параметры – функционал оборудования на уровне драйвера. Допустим, audio – будет ли у нас видеокарта работать со звуком через тот же hdmi.

Так вот, недавно мы узнали о виртуальной файловой системе procfs, через которую ядро нам показывает информацию о процессах. А для структурированной информации об устройствах и драйверах есть виртуальная файловая система sysfs, доступная в директории /sys:

ls /sys
ls /sys/bus/pci/device/00*

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

Например:

показывает информацию о процессоре,

показывает информацию об устройствах, подключённых на pci шину,

устройства, подключённые к usb. Для более подробной информации вы можете использовать lshw:

а из графических утилит есть hardinfo.

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

Запустили команду, вставили флешку или любое другое устройство, и тут вы увидите, как ядро распознаёт устройство.

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

И так, для управления модулями ядра используется утилита:

Допустим, если хотим загрузить модуль radeon, пишем:

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

Одни модули могут работать с другими и сами могут использоваться какими-то процессами. Например, чтобы выгрузить модуль из ядра, можно использовать тот же modprobe с ключом -r:

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

sudo modprobe -r vboxguest

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

Так вот, возвращаясь к теме про автоматическую загрузку модулей. В системе есть программа, называемая udev – именно она отвечает за управление устройствами. И когда ядро видит новое устройство, оно создаёт событие, которое отслеживает udev. У udev есть большое количество правил:

ls /usr/lib/udev/rules.d/

которые оно применяет при виде того или иного события. Например, udev видит в событии, что в usb подключено устройство, которое говорит что оно является накопителем – и у udev есть правило, которое при виде такого устройства создаёт для него специальный файл в директории /dev с таким-то названием – допустим, sdb. Этот файл ядро связывает с драйвером, работающим с оборудованием. Таким образом наша флешка становится файлом, благодаря чему мы через этот файл можем взаимодействовать с устройством. Точно также можно в udev прописать, чтобы при виде usb устройства для него передавался специальный параметр, который бы запрещал устройству что-либо делать. Углубляться в udev мы пока не будем, для начала хватит понимания, зачем он вообще нужен.

Модули можно скомпилировать в ядро – тогда отпадает необходимость загружать модули из файлов, но это увеличивает размер ядра. Такие модули называются встроенными и их список можно увидеть в файле modules.builtin:

cat /lib/modules/$(uname -r)/modules.builtin

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

Иногда может понадобится, чтобы загружаемые модули загружались всегда при включении компьютера. Для этого нужно создать файл в директории /etc/modules-load.d/ c .conf в конце имени, допустим, radeon.conf:

sudo nano /etc/modules-load.d/radeon.conf

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

Если же нам нужно, чтобы какой-то определённый модуль не загружался при включении или загружался с определёнными параметрами – теми параметрами, которые мы видели с modinfo radeon — то для этого нужно создать файл в директории /etc/modprobe.d тоже заканчивающийся на .conf, например, kvm.conf:

sudo nano /etc/modprobe.d/kvm.conf

Тут у нас есть закомментированные примеры. Чуть подробнее об этом можно почитать на арчвики.

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

AMD64 Handbook
Установка
Об установке
Выбор подходящего источника для установки
Настройка сети
Подготовка дисков
Установка stage3
Установка базовой системы
Настройка ядра
Настройка системы
Установка системных утилит
Настройка загрузчика
Завершение
Работа с Gentoo
Введение в Portage
USE-флаги
Возможности Portage
Система сценариев инициализации
Переменные окружения
Работа с Portage
Файлы и каталоги
Переменные
Смешение ветвей программного обеспечения
Дополнительные утилиты
Дополнительные репозитории пакетов
Расширенные возможности
Настройка сети
Начальная настройка
Расширенная настройка
Модульное построение сети
Беспроводная сеть
Добавляем функциональность
Динамическое управление

Необязательно: Установка файлов прошивки и/или микрокода

Файлы прошивки

Перед тем, как приступить к настройке ядра, полезно будет помнить, что некоторые аппаратные устройства требуют установки в систему дополнительной, иногда не совместимой с принципами FOSS (free (as in freedom) and open source software/свободное и открытое программное обеспечение), прошивки, прежде чем они будут работать правильно. Чаще всего это касается беспроводных сетевых интерфейсов, обычно встречающихся как в настольных, так и в портативных компьютерах. Современные видеочипы от таких производителей, как AMD, Nvidia и Intel также часто требуют установки внешней прошивки для обеспечения полной функциональности. Большинство прошивок для современных аппаратных устройств можно найти в пакете sys-kernel/linux-firmware.

Рекомендуется установить пакет sys-kernel/linux-firmware перед первоначальной перезагрузкой системы, чтобы прошивка была доступна в случае необходимости:

root #emerge --ask sys-kernel/linux-firmware

Заметка
Установка определённых пакетов прошивок часто требует принятия соответствующих лицензий на прошивку. При необходимости посетите раздел руководства о принятии лицензии для получения помощи.

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

Микрокод

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

Обновления микрокода для процессоров AMD распространяются в вышеупомянутом пакете sys-kernel/linux-firmware. Обновления микрокода для процессоров Intel находятся в пакете sys-firmware/intel-microcode, который необходимо установить отдельно. См. статью Микрокол для получения дополнительной информации о том, как применять обновления микрокода.

Конфигурация и компиляция ядра

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

От наименьшего вмешательства к наибольшему:

Полностью автоматический подход: Distribution-ядра
Проект Distribution Kernel используется для конфигурации, автоматической сборки и установки ядра Linux, связанных с ним модулей и (опционально, но по умолчанию включено) файла initramfs. Новые обновления ядра полностью автоматизированы, поскольку они обрабатываются через менеджер пакетов, как и любой другой системный пакет. В случае необходимости можно предоставить пользовательский конфигурационный файл ядра. Это наименее сложный процесс и идеально подходит для новых пользователей Gentoo, так как работает «из коробки» и требует минимального участия системного администратора.
Гибридный подход: Genkernel
Новые обновления ядра устанавливаются через системный менеджер пакетов. Системные администраторы используют инструмент Gentoo genkernel для общей конфигурации, автоматической сборки и установки ядра Linux, связанных с ним модулей и (опционально, но не включено по умолчанию) файла initramfs. Можно предоставить пользовательский файл конфигурации ядра, если необходима кастомизация. Будущая конфигурация, сборка и установка ядра требуют участия системного администратора в виде выполнения eselect kernel, genkernel и, возможно, других команд для каждого обновления.
Полностью ручная настройка
Новые исходные тексты ядра устанавливаются с помощью системного менеджера пакетов. Ядро конфигурируется, собирается и устанавливается вручную с помощью команды eselect kernel и множества команд make. С новыми обновлениями ядра повторяется ручной процесс конфигурирования, сборки и установки файлов ядра. Это самый сложный процесс, но он обеспечивает максимальный контроль над процессом обновления ядра.

Основой, вокруг которой строятся все дистрибутивы, является ядро Linux. Оно является прослойкой между пользовательскими программами и аппаратным обеспечением системы. Хотя руководство предоставляет своим пользователям несколько возможных источников ядра, более подробная информация с более детальным описанием доступна на странице {{|Link|Kernel/Overview|Общие сведения о ядре}}.

Distribution-ядра

Distribution-ядра — это ebuild-файлы, которые охватывают полный процесс распаковки, конфигурирования, компиляции и установки ядра. Основным преимуществом этого метода является то, что ядра обновляются до новых версий менеджером пакетов во время обновления @world. Для этого используется только команда emerge. Distribution-ядра по умолчанию сконфигурированы для поддержки большинства оборудования, для более тонкой настройки предлагаются два механизма: saveconfig и сниппеты конфигурации. Смотрите страницу проекта для более подробной информации о конфигурации.

Установка подходящего пакета installkernel

Перед использованием distribution-ядер убедитесь, что установлен подходящий для системы пакет installkernel. При использовании systemd-boot (ранее gummiboot) в качестве загрузчика, установите:

root #emerge --ask sys-kernel/installkernel-systemd-boot

При использовании традиционной схемы /boot (например, GRUB, LILO и т.д.), вариант gentoo должен быть установлен по умолчанию. Если вы не уверены:

root #emerge --ask sys-kernel/installkernel-gentoo

Установка distribution-ядра

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

root #emerge --ask sys-kernel/gentoo-kernel

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

root #emerge --ask sys-kernel/gentoo-kernel-bin

Обновление и очистка

После установки ядра менеджер пакетов будет автоматически обновлять его до более новых версий. Предыдущие версии будут храниться до тех пор, пока менеджер пакетов не получит запрос на очистку устаревших пакетов. Чтобы освободить место на диске, устаревшие пакеты можно удалить, периодически запуская emerge с опцией --depclean:

Также можно удалить именно устаревшие ядра:

root #emerge --prune sys-kernel/gentoo-kernel sys-kernel/gentoo-kernel-bin

Задачи после установки/обновления

Distribution-ядра способны пересобирать модули ядра, установленные другими пакетами. linux-mod.eclass предоставляет USE-флаг dist-kernel, который управляет зависимостью от подслота virtual/dist-kernel.

Включение этого USE-флага для таких пакетов, как sys-fs/zfs и sys-fs/zfs-kmod позволит им автоматически пересобираться в соответствии с обновленным ядром и, в случае необходимости, пересобирать initramfs.

Ручная пересборка initramfs

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

root #emerge --ask @module-rebuild

Если какой-то модуль ядра (например, ZFS) необходим при ранней загрузке, пересоберите initramfs при помощи:

root #emerge --config sys-kernel/gentoo-kernel

root #emerge --config sys-kernel/gentoo-kernel-bin

Установка исходного кода ядра

Заметка
Этот раздел актуален только при использовании следующих методов genkernel (гибридного) или ручного подхода к управлению ядром

.

При установке и компиляции ядра для систем на базе amd64 Gentoo рекомендует использовать пакет sys-kernel/gentoo-sources.

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

root #emerge --ask sys-kernel/gentoo-sources

Данная команда установит исходный код ядра Linux в /usr/src/, используя в названии версию ядра. Эта команда не установит автоматически символьную ссылку, пока вы не укажете USE=symlink для выбранного исходного кода ядра.

Обычно, символьная ссылка /usr/src/linux указывает на исходный код текущего работающего ядра. Однако, эта символьная ссылка не создаётся по умолчанию. Создать её поможет kernel модуль для eselect.

Чтобы подробнее узнать, зачем нужна эта символьная ссылка и как ею управлять, смотрите Kernel/Upgrade.

Для начала, просмотрите список установленных ядер (в виде исходного кода):

root #eselect kernel list

Available kernel symlink targets:
  [1]   linux-5.15.52-gentoo

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

root #eselect kernel set 1

root #ls -l /usr/src/linux

lrwxrwxrwx    1 root   root    20 мар  3 22:44 /usr/src/linux -> linux-5.15.52-gentoo

Альтернатива: Genkernel

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

Genkernel предоставляет базовый файл конфигурации ядра, автоматически генерирует ядро ( generates the kernel), initramfs и связанные модули, а затем устанавливает полученные двоичные файлы в соответствующие места. Это обеспечивает минимальную и базовую аппаратную поддержку при первой загрузке системы, а в дальнейшем позволяет дополнительно контролировать обновление и настраивать конфигурацию ядра.

Учтите: хотя использование genkernel для поддержки ядра обеспечивает системным администраторам больший контроль над обновлением ядра системы, initramfs и других опций, это требует затрат времени и усилий для выполнения будущих обновлений ядра по мере выпуска новых источников. Тем, кто ищет автоматический подход к обслуживанию ядра, следует использовать distribution-ядра.

Для большей ясности, это является заблуждением, что genkernel автоматически генерирует специальную конфигурацию ядра для оборудования, на котором он запущен; он использует определённую конфигурацию ядра, которая поддерживает большинство оборудования и автоматически обрабатывает команды make, необходимые для сборки и установки ядра, сопутствующих модулей и файла initramfs.

Группа лицензий на «программное обеспечение, распространяемое в бинарном виде»

Если пакет linux-firmware был уже установлен, перейдите к разделу установки.

Поскольку по умолчанию для пакета sys-kernel/genkernel включен USE-флаг firwmare, пакетный менеджер также попытается установить пакет sys-kernel/linux-firmware. Перед установкой linux-firmware необходимо принять лицензии на «программное обеспечение, распространяемое в бинарном виде».

Эта группа лицензий может быть принята для всей системы путем добавления @BINARY-REDISTRIBUTABLE в переменную ACCEPT_LICENSE в файле /etc/portage/make.conf. Лицензия также может быть принята только для пакета linux-firmware с помощью добавления в файле /etc/portage/package.license/linux-firmware.

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

Для упрощения, примеры разрешения лицензий:

root #mkdir /etc/portage/package.license

ФАЙЛ /etc/portage/package.license/linux-firmwareРазрешения лицензий на «программное обеспечение, распространяемое в бинарном виде» для linux-firmware

sys-kernel/linux-firmware @BINARY-REDISTRIBUTABLE

Установка

Итак, установите пакет sys-kernel/genkernel:

root #emerge --ask sys-kernel/genkernel

Генерация

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

Заметка
Если для корневого раздела/тома используется файловая система, отличная от ext4, может потребоваться вручную настроить ядра с помощью genkernel —menuconfig all, чтобы добавить встроенную поддержку ядра для данной файловой системы (т.е. не собирать файловую систему как модуль).

Заметка
Пользователи LVM2 должны добавить --lvm в качестве аргумента к команде genkernel ниже

.

root #genkernel --mountboot --install all

По завершению работы genkernel будут сформированы ядро, полный набор модулей и файловая система инициализации (initramfs). Ядро и initrd нам понадобятся позднее. Запишите название файлов ядра и initrd, так как они нам понадобятся при настройке загрузчика. Initrd запускается сразу после ядра для определения оборудования (как при загрузке установочного CD), перед запуском самой системы.

После завершения работы genkernel, ядро и начальная файловая система ram (initramfs) будут сформированы и установлены в каталог /boot. Соответствующие модули будут установлены в каталог /lib/modules. initramfs будет запущена сразу после загрузки ядра для автоматического определения оборудования (как при загрузке «живого» (live) загрузочного диска).

root #ls /boot/vmlinu* /boot/initramfs*

root #ls /lib/modules

Альтернатива: Ручная настройка

Введение

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

Однако одна вещь является истиной: при ручной конфигурации ядра очень важно понимать свою систему. Большую часть сведений можно почерпнуть, установив пакет sys-apps/pciutils, который содержит в команду lspci:

root #emerge --ask sys-apps/pciutils

Заметка
Находясь внутри изолированного окружения chroot, можно спокойно игнорировать любые предупреждения pcilib (например, pcilib: cannot open /sys/bus/pci/devices), которые могут появляться в выводе lspci.

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

Остаётся перейти в каталог с ядром и выполнить make menuconfig, который запустит экран меню конфигурации.

root #cd /usr/src/linux

root #make menuconfig

В конфигурации ядра Linux есть много-много разделов. Сначала пройдёмся по пунктам, которые должны быть обязательно включены (иначе Gentoo будет работать неправильно или же вовсе не запустится). Также в вики есть Руководство по настройке ядра Gentoo, которое поможет понять более тонкие детали.

Включение обязательных параметров

При использовании sys-kernel/gentoo-sources, строго рекомендуется включить Gentoo-специфичные настройки. С помощью них включается необходимый минимум настроек ядра для корректной работы:

ЯДРО Включение Gentoo-специфичных настроек

Gentoo Linux --->
  Generic Driver Options --->
    [*] Gentoo Linux support
    [*]   Linux dynamic and persistent device naming (userspace devfs) support
    [*]   Select options required by Portage features
        Support for init systems, system and service managers  --->
          [*] OpenRC, runit and other script based systems and managers
          [*] systemd

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

При использовании sys-kernel/vanilla-sources, этих вспомогательных настроек не будет. Вы можете включить нужные настройки вручную, но это выходит за рамки данного руководства.

Включение поддержки основных компонентов системы

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

Далее следует выбрать тип процессора. Также рекомендуется включить возможности MCE (если они доступны), чтобы пользователи системы могли получать оповещение о любых проблемах с оборудованием. На некоторых архитектурах (например, x86_64) подобные ошибки выводятся не в dmesg, а в /dev/mcelog. А для него понадобится пакет app-admin/mcelog.

Также включите Maintain a devtmpfs file system to mount at /dev, чтобы критически важные файлы устройств были доступны на самом раннем этапе загрузки (CONFIG_DEVTMPFS и CONFIG_DEVTMPFS_MOUNT):

ЯДРО Включение поддержки devtmpfs (CONFIG_DEVTMPFS)

Device Drivers --->
  Generic Driver Options --->
    [*] Maintain a devtmpfs filesystem to mount at /dev
    [*]   Automount devtmpfs at /dev, after the kernel mounted the rootfs

Убедитесь, что поддержка SCSI-дисков включена (CONFIG_BLK_DEV_SD):

ЯДРО Включение поддержки SCSI-дисков (CONFIG_SCSI, CONFIG_BLK_DEV_SD)

Device Drivers --->
  SCSI device support  ---> 
    <*> SCSI device support
    <*> SCSI disk support

ЯДРО Включение базовой поддержки SATA и PATA (CONFIG_ATA_ACPI, CONFIG_SATA_PMP, CONFIG_SATA_AHCI, CONFIG_ATA_BMDMA, CONFIG_ATA_SFF, CONFIG_ATA_PIIX)

Device Drivers --->
  <*> Serial ATA and Parallel ATA drivers (libata)  --->
    [*] ATA ACPI Support
    [*] SATA Port Multiplier support
    <*> AHCI SATA support (ahci)
    [*] ATA BMDMA support
    [*] ATA SFF support (for legacy IDE and PATA)
    <*> Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support (ata_piix)

Убедитесь, что включена базовая поддержка NVMe:

ЯДРО Включение базовой поддержки NVMe для Linux 4.4.x (CONFIG_BLK_DEV_NVME)

Device Drivers  --->
  <*> NVM Express block device

ЯДРО Включение базовой поддержки NVMe для Linux 5.x.x (CONFIG_DEVTMPFS)

Device Drivers --->
  NVME Support --->
    <*> NVM Express block device

Не помешает включить следующую дополнительную поддержку NVMe:

ЯДРО Включение дополнительной поддержки NVMe (CONFIG_NVME_MULTIPATH, CONFIG_NVME_MULTIPATH, CONFIG_NVME_HWMON, CONFIG_NVME_FC, CONFIG_NVME_TCP, CONFIG_NVME_TARGET, CONFIG_NVME_TARGET_PASSTHRU, CONFIG_NVME_TARGET_LOOP, CONFIG_NVME_TARGET_FC, CONFIG_NVME_TARGET_FCLOOP, CONFIG_NVME_TARGET_TCP

[*] NVMe multipath support
[*] NVMe hardware monitoring
<M> NVM Express over Fabrics FC host driver
<M> NVM Express over Fabrics TCP host driver
<M> NVMe Target support
  [*]   NVMe Target Passthrough support
  <M>   NVMe loopback device support
  <M>   NVMe over Fabrics FC target driver
  < >     NVMe over Fabrics FC Transport Loopback Test driver (NEW)
  <M>   NVMe over Fabrics TCP target support

Теперь перейдите в раздел File Systems и выберите те файловые системы, которые планируете использовать. Файловая система, используемая в качестве корневой, должна быть включена в ядро (не модулем), иначе система не сможет подключить раздел при загрузке. Также включите Virtual memory и /proc file system. При необходимости выберите один или несколько параметров, необходимых системе:

ЯДРО Включение поддержки файловой системы (CONFIG_EXT2_FS, CONFIG_EXT3_FS, CONFIG_EXT4_FS, CONFIG_BTRFS_FS, CONFIG_MSDOS_FS, CONFIG_VFAT_FS, CONFIG_PROC_FS, и CONFIG_TMPFS)

File systems --->
  <*> Second extended fs support
  <*> The Extended 3 (ext3) filesystem
  <*> The Extended 4 (ext4) filesystem
  <*> Btrfs filesystem support
  DOS/FAT/NT Filesystems  --->
    <*> MSDOS fs support
    <*> VFAT (Windows-95) fs support
  Pseudo Filesystems --->
    [*] /proc file system support
    [*] Tmpfs virtual memory file system support (former shm fs)

Если для подключения к Интернету используется PPPoE или модемное соединение, то включите следующие параметры (CONFIG_PPP, CONFIG_PPP_ASYNC и CONFIG_PPP_SYNC_TTY):

ЯДРО Включение поддержки PPPoE (PPPoE, CONFIG_PPPOE, CONFIG_PPP_ASYNC, CONFIG_PPP_SYNC_TTY

Device Drivers --->
  Network device support --->
    <*> PPP (point-to-point protocol) support
    <*> PPP over Ethernet
    <*> PPP support for async serial ports
    <*> PPP support for sync tty ports

Два параметра сжатия не повредят, но и не являются обязательными, как и PPP over Ethernet. Фактически, последний используется в случае, если ppp сконфигурирован на использование ядерный PPPoE режим.

Не забудьте настроить поддержку сетевых карт (Ethernet или беспроводных).

Поскольку большинство современных систем являются многоядерными, важно включить Symmetric multi-processing support (CONFIG_SMP):

ЯДРО Включение поддержки SMP (CONFIG_SMP)

Processor type and features  --->
  [*] Symmetric multi-processing support

Заметка
Во многоядерных системах каждое ядро считается за один процессор.

Если используются USB-устройства ввода (например, клавиатура и мышь) или другие устройства, то не забудьте включить и эти параметры:

ЯДРО Включение поддержки USB и HID(CONFIG_HID_GENERIC, CONFIG_USB_HID, CONFIG_USB_SUPPORT, CONFIG_USB_XHCI_HCD, CONFIG_USB_EHCI_HCD, CONFIG_USB_OHCI_HCD, (CONFIG_HID_GENERIC, CONFIG_USB_HID, CONFIG_USB_SUPPORT, CONFIG_USB_XHCI_HCD, CONFIG_USB_EHCI_HCD, CONFIG_USB_OHCI_HCD, CONFIG_USB4)

Device Drivers --->
  HID support  --->
    -*- HID bus support
    <*>   Generic HID driver
    [*]   Battery level reporting for HID devices
      USB HID support  --->
        <*> USB HID transport layer
  [*] USB support  --->
    <*>     xHCI HCD (USB 3.0) support
    <*>     EHCI HCD (USB 2.0) support
    <*>     OHCI HCD (USB 1.1) support
  <*> Unified support for USB4 and Thunderbolt  --->

Настройка ядра, специфичная для архитектуры

Если необходимо поддерживать 32-битные программы, убедитесь, что выбран пункт IA32 Emulation (CONFIG_IA32_EMULATION). Gentoo устанавливает систему multilib (смешанные 32/64 битные вычисления) по умолчанию, поэтому данная опция необходима, если конечно не выбран профиль no-multilib.

ЯДРО Выбор типа процессора и возможностей

Processor type and features  --->
   [ ] Machine Check / overheating reporting 
   [ ]   Intel MCE Features
   [ ]   AMD MCE Features
   Processor family (AMD-Opteron/Athlon64)  --->
      ( ) Opteron/Athlon64/Hammer/K8
      ( ) Intel P4 / older Netburst based Xeon
      ( ) Core 2/newer Xeon
      ( ) Intel Atom
      ( ) Generic-x86-64
Binary Emulations --->
   [*] IA32 Emulation

Включите поддержку меток GPT, если использовали их ранее при разбиении диска (CONFIG_PARTITION_ADVANCED и CONFIG_EFI_PARTITION):

ЯДРО Включение поддержки GPT

-*- Enable the block layer --->
   Partition Types --->
      [*] Advanced partition selection
      [*] EFI GUID Partition support

Включите поддержку EFI stub, EFI variables и EFI Framebuffer в ядре Linux, если для загрузки системы используется UEFI (CONFIG_EFI, CONFIG_EFI_STUB, CONFIG_EFI_MIXED, CONFIG_EFI_VARS и CONFIG_FB_EFI):

ЯДРО Включение поддержки UEFI

Processor type and features  --->
    [*] EFI runtime service support 
    [*]   EFI stub support
    [*]     EFI mixed-mode support
 
Device Drivers
    Firmware Drivers  --->
        EFI (Extensible Firmware Interface) Support  --->
            <*> EFI Variable Support via sysfs
    Graphics support  --->
        Frame buffer Devices  --->
            <*> Support for frame buffer devices  --->
                [*]   EFI-based Framebuffer Support

Компиляция и установка

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

root #make && make modules_install

Заметка
Можно включить параллельную сборку, используя make -jX, где X — это число параллельных задач, которые может запустить процесс сборки. Это похоже на инструкции, которые были даны ранее относительно файла /etc/portage/make.conf в части переменной MAKEOPTS.

По завершении компиляции, скопируйте образ ядра в каталог /boot/. Это делается командой make install:

Данная команда скопирует образ ядра в каталог /boot/ вместе с файлом System.map и файлом настройки ядра.

Необязательно: Сборка initramfs

В некоторых случаях необходимо собрать initramfs — файловую систему инициализации, размещаемую в оперативной памяти. Самая частая причина — когда важные части системных путей (например, /usr/ или /var/) находятся на отдельных разделах. Эти разделы могут быть смонтированы средствами, расположенными внутри initramfs.

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

Важно
Если вы используете genkernel, вы должны использовать его для сборки и ядра, и initramfs. Если вы используете genkernel только для сборки initramfs, вы должны добавить аргумент --kernel-config=/путь/к/ядру.config к genkernel, иначе собранный initramfs может не заработать с вручную собранным ядром. Обратите внимание, что вручную собранные ядра выходят за рамки данного руководства. Смотрите страницу про настройку ядра для более подробной информации.

Чтобы установить initramfs, сперва установите sys-kernel/dracut, затем сгенерируйте initramfs:

root #emerge --ask sys-kernel/dracut

root #dracut --kver=5.15.52-gentoo

initramfs будет сохранён в /boot/ под названием, начинающимся с «initramfs»:

root #ls /boot/initramfs*

Теперь продолжите с раздела Модули ядра.

Модули ядра

Список доступных модулей ядра

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

Модули, которые должны загружаться при каждой загрузке, могут быть добавлены в файлы /etc/modules-load.d/*.conf, по одному модулю в строке. Если для модулей необходимы дополнительные параметры, их следует указывать в файлах /etc/modprobe.d/*.conf.

Чтобы просмотреть все модули, доступные для определённой версии ядра, выполните следующую команду find. Не забудьте заменить «<kernel version>» на соответствующую версию ядра для поиска:

root #find /lib/modules/<kernel version>/ -type f -iname '*.o' -or -iname '*.ko' | less

Принудительная загрузка отдельных модулей ядра

Чтобы принудительно загрузить в систему модуль 3c59x.ko (драйвер для определённого семейства сетевых карт от 3Com), отредактируйте файл /etc/modules-load.d/network.conf и добавьте туда имя модуля.

root #mkdir -p /etc/modules-load.d

root #nano -w /etc/modules-load.d/network.conf

Обратите внимание, что суффикс в имени файла модуля .ko несущественен для механизма загрузки и не включается в файл:

ФАЙЛ /etc/modules-load.d/network.confПринудительная загрузка модуля 3c59x

3c59x

Продолжите установку с раздела Настройка системы.

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


Сводка

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

Имя статьи

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

Описание

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

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

Установка утилит

Для настройки и сборки ядра Linux вам потребуется установить несколько пакетов, которые понадобятся для сборки и настройки ядра: kernel-package, build-essential, libncurses-dev. Сделать это можно командой:

sudo apt-get install build-essential kernel-package libncurses-dev

Скачиваем исходный код ядра

Теперь нужно скачать исходный код ядра. Мы будем скачивать ядро для Ubuntu. Вы можете скачать определенную версию ядра, например, ту, которую вы в данный момент используете или же скачать самую последнюю версию. Для того, чтобы определить версию ядра Linux, которую вы используете, выполните команду uname с параметром -r:

uname -r

Вывод команды будет примерно следующим:

$uname -r
2.6.27-11-generic

Имя пакета, содержащего исходные коды ядра обычно имеет следующий вид: linux-source-Версия. Например, для ядра версии 2.6.24: linux-source-2.6.24. Самая последняя версия ядра в репозиториях Ubuntu называется просто linux-source, без указания версии на конце. Для установки исходных кодов последней версии ядра Ubuntu Linux, выполните команду:

sudo apt-get install linux-source

Эта команда скачивает исходники ядра и размещает их в директории /usr/src. На момент написания заметки последняя версия ядра, которая была скачана — 2.6.27, ее мы и будем использовать. Если мы теперь перейдем в директорию /usr/src и выполним команду ls, то увидим, что среди файлов присутствует файл linux-source-2.6.27.tar.bz2. Это и есть исходные коды ядра Linux (ядра Ubuntu).

Распаковываем исходный код ядра

Перейдем в директорию /usr/src и разархивируем ядро. Для этого выполните следующие команды:

cd /usr/src
sudo tar xjf linux-source-2.6.27.tar.bz2
sudo ln -s linux-source-2.6.27 linux

Для удобства мы создали символьную ссылку с именем linux, которая указывает на директорию с исходниками.

Конфигурация ядра

Теперь перейдем к конфигурированию ядра. Чтобы не создавать конфигурацию с нуля, возьмем за основу конфигурацию ядра, которая в данный момент используется. Получить текущую конфигурацию можно выполнив команду make oldconfig. Выполните в терминале:

cd /usr/src/linux
sudo make oldconfig

В результате выполнения команды make oldconfig создастся файл .config, содержащий параметры конфигурации ядра.

Получить справку по всем параметрам make для ядра Linux вы можете, выполнив команду make help.

Для изменения конфигурации ядра мы воспользуемся консольной утилитой menuconfig. Для ее запуска выполните:

sudo make menuconfig

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

Утилита Menuconfig

Для примера я включу опцию «NTFS write support». Для этого, нажимая кнопку Вниз, найдите пункт «File systems» и нажмите Enter.

Настройка ядра Linux - File systems

Вы окажетесь в меню настройки файловых систем. Найдите в этом списке пункт «DOS/FAT/NT Filesystems» и нажмите Enter.

Настройка ядра Linux - DOS/FAT/NT Filesystems

Перейдите к пункту «NTFS write support» и нажмите Пробел, рядом с пунктом появится звездочка, означающая, что данная опция будет включена в ядро.

Настройка ядра Linux - NTFS write support

Теперь выберите «Exit» (нажав кнопку Вправо и затем Enter) и выйдите из утилиты. Перед выходом из утилиты выскочит сообщение с вопросом — сохранить проделанные изменения, выберите Yes.

Компиляция ядра

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

sudo make-kpkg clean

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

sudo make-kpkg --initrd --append-to-version=-mykernel kernel_image kernel_headers

Ключ -append-to-version используется, чтобы добавить к имени файла образа ядра, который мы получим после компиляции, строку -mykernel, чтобы было проще идентифицировать свое ядро. Вместо -mykernel вы можете использовать любой префикс.

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

Установка (инсталляция) ядра

После компиляции ядра вы получили на выходе два файла: linux-image-2.6.27.18-mykernel_2.6.27.18-mykernel-10.00.Custom_i386.deb, linux-headers-2.6.27.18-mykernel_2.6.27.18-mykernel-10.00.Custom_i386.deb. Мы воспользуемся командной dpkg -i, которая автоматически установит ядро и пропишет его в ваш загрузчик GRUB (в файл /boot/grub/menu.lst). Отмечу, что ядро будет установлено, как ядро по умолчанию, поэтому если оно у вас не загрузится вам нужно будет загрузиться, используя ваше предыдущее ядро (оно должно быть в списке меню GRUB при загрузке компьютера) и вручную изменять файл menu.lst. Итак, для установки ядра выполните команды:

dpkg -i linux-image-2.6.27.18-mykernel_2.6.27.18-mykernel-10.00.Custom_i386.deb
dpkg -i linux-headers-2.6.27.18-mykernel_2.6.27.18-mykernel-10.00.Custom_i386.deb

Запуск системы с новым ядром

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

3.1. Как устроен Linux: ядро и процессы

Главная, постоянно находящаяся в оперативной памяти, часть ОС Linux называется ядром (Kernel). Ядро ОС обрабатывает прерывания от устройств, выполняет запросы системных процессов и пользовательских приложений, распределяет виртуальную память, создает и уничтожает процессы, обеспечивает многозадачность посредством переключения между ними, содержит драйверы устройств, обслуживает файловую систему (см. рис. 3.1).

Рис. 3.1. Устройство ОС Linux

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

Начальная загрузка системы состоит в том, что файл с образом ядра считывается в оперативную память, начиная с нулевого адреса. Этот файл находится в каталоге /boot и называется vmlinuz-x.y.z, где x.y.z — это номер версии ядра. На текущий момент большинство дистрибутивов основано на ядре версии 2.4, хотя уже вышло ядро 2.6 (Fedora Core 3) и кое-где еще встречается версия 2.2.

Примечание

По соглашению разработчиков ядра, все ветви с четным номером (2.2, 2.4 и т.д.) считаются стабильными и рекомендуются для широкого использования, а на ветвях с нечетным номером испытываются новые идеи, Линус Торвальдс предложил распространить эту схему нумерации и на третью цифру версии: в ядра с нечетными номерами добавлять новые функции, а в четных — только исправлять обнаруженные ошибки.

В UNIX-подобных системах в отличие от других ОС ядро минимизировано и не выполняет ни одной функции, служащей непосредственно пользователю. Для этой цели применяются многочисленные утилиты, выступающие в качестве посредников между пользователем и ядром. Только в комплекте с ними ядро образует полноценную операционную систему.

Этих компонент ОС Линус Торвальдс не создавал: они поступили из проекта GNU (http://www.gnu.org), участники которого с 1984 года работают над созданием полноценной UNIX-подобной ОС, целиком состоящей из свободно распространяемого программного обеспечения. К 1991 году им не хватало только ядра, и эту-то прореху и заполнил Торвальдс. Так что ОС, которой посвящена эта книга, правильнее называть не Linux, а «операционной системой GNU, основанной на ядре Linux», или просто GNU/Linux.

Итак, ядро обслуживает запросы процессов. Что же такое процесс? Это понятие является базовым в UNIX-подобных системах. Процесс можно представить себе как виртуальную машину, отданную в распоряжение одной задачи. Каждый процесс считает, что он на машине один и может распоряжаться всеми ее ресурсами. На самом же деле процессы надежно изолированы друг от друга, так что крушение одного не может повредить всей системе (сколько раз вы наблюдали в Windows, как сбой одной программы приводил к общему зависанию?).

Каждый процесс выполняется в собственной виртуальной памяти (см, рис. 3.2), в которую никакой другой процесс вмешаться не может. Этим и обеспечивается устойчивость всей системы.

Рис. 3.2. Виртуальная память процесса

Напоминаю, что такое виртуальная память. Каждому процессу разрешено считать, что его адреса начинаются с нулевого адреса и от него наращиваются. Таким образом, в 32-разрядной ОС процесс может адресовать 4 гигабайта оперативной памяти. Механизм виртуальной памяти позволяет процессу думать, что именно столько ему и выделено, хотя физически объем ОЗУ вашей машины — какие-то жалкие 256 Мбайт. Недостающую память заменяет жесткий диск путем записи временно не используемых страниц памяти в раздел подкачки (свопинга).

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

Данный текст является ознакомительным фрагментом.

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

Глава 1 Введение в ядро Linux

Глава 1
Введение в ядро Linux
Даже после трех десятилетий использования операционная система (ОС) Unix все еще считается одной из самых мощных и элегантных среди всех существующих операционных систем. Со времени создания операционной системы Unix в 1969 году, это детище Денниса

Ядро Linux в сравнении с классическими ядрами Unix

Ядро Linux в сравнении с классическими ядрами Unix
Благодаря общему происхождению и одинаковому API, современные ядра Unix имеют некоторые общие характерные черты. За небольшими исключениями ядра Unix представляют собой монолитные статические бинарные файлы. Это значит, что они

Ядро в роли арбитра

Ядро в роли арбитра
Так кто же определяет, который из потоков должен выполняться в данный момент времени? Этим занимается ядро.Ядро определяет, который из потоков должен использовать процессор в данный момент времени и переключает контекст на этот поток. Давайте

Ядро системы

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

IPLabs Linux Team: начало русского Linux’а

IPLabs Linux Team: начало русского Linux’а
Следующая веха на пути русского Linux’а – 1998 год, когда фирма IPLabs (точнее, ее подразделение – IPLabs Linux Team) совместно с Институтом логики (на самом деле это были одни и те же люди – Алексей Новодворский, Алексей Смирнов и Юрий Девяткин с

Linux (ядро)

Linux (ядро)
Официальная ссылкаLinux (2.4.19): ftp://ftp.kernel.org/pub/linux/kernel/Содержимое LinuxПоследняя проверка: версия 2.4.18.Файлы поддержкиЯдро Linux и и его заголовочные файлыОписанияЯдро LinuxЯдро – основа любой системы Linux. Когда компьютер включается и загружает Linux, первое, что загружается –

1.4. Ядро

1.4. Ядро
Ядро — это сердце ОС, в котором реализовано управление физическими и программными ресурсами компьютера. Помимо этого оно позволяет получить доступ к различному железу. Например, ранние версии ядра обеспечивали работу только двух USB-устройств: клавиатура и мышь.

20.5.1. Зачем обновлять ядро?

20.5.1. Зачем обновлять ядро?
Linux развивается быстрее любой другой операционной системы. Регулярно появляются новые версии ядра, реализующие новые функции. Например, едва успел выйти дистрибутив Fedora Core 4 на ядре 2.6.11, а на www.kernel.org уже лежит стабильная версия 2.6.12.2. Еще чаще

Семантическое ядро

Семантическое ядро
Анализ семантического ядра на полноту охвата. Очень важно, чтобы семантическое ядро охватывало максимально возможное количество проблем пользователей и запросов, формирующих эти проблемы. Нужно выяснить:? охватывает ли семантическое ядро весь

Составьте семантическое ядро

Составьте семантическое ядро
Чтобы успешно продвигать свой бизнес в Интернете, необходимо правильно составить семантическое ядро.Семантическое ядро, говоря простым языком, – это слова, которые будут наиболее точным образом характеризовать направленность вашей

Глава 1 Как устроен компьютер

Глава 1 Как устроен компьютер
Эта глава посвящена обзору аппаратных средств персонального компьютера. В ней мы также кратко познакомимся с понятием программного обеспечения и узнаем о назначении операционной системы.• Аппаратные средства ПК• Как включить

Ядро API Firebird

Ядро API Firebird
Программирование с использованием API необходимо при написании драйверов для создания сценариев в таких языках, как PHP и Python, и при разработке объектно- ориентированных классов доступа к данным для объектно-ориентированных языков типа Java, C++ и Object Pascal.

Как устроен Internet

Как устроен Internet
Internet качественно отличается от телеграфа, телефона, газет, радио и телевидения: информация здесь не только доставляется адресату, но и просто предоставляется на всеобщее обозрение, и каждый может сам найти и воспользоваться именно тем, что ему интересно.

Технология мысленных приказов: как устроен первый интерфейс «мозг — мозг» Андрей Васильков

Технология мысленных приказов: как устроен первый интерфейс «мозг — мозг»

Андрей Васильков

Опубликовано 29 августа 2013
Исследователи из университета штата Вашингтон провели необычный эксперимент, который можно считать первым в истории случаем

Понравилась статья? Поделить с друзьями:
  • Цефтриаксон чем разбавлять инструкция по применению уколы цена
  • Эдиками таблетки инструкция по применению цена
  • Руководство по эксплуатации вмз
  • Корвалол инструкция таблетки по применению взрослым от чего помогает взрослым
  • Руководство мегафон ритейла