/
bvs22
/
draft
Обзор
Документация
Войти
/
bvs22
/
draft
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
kernel/kernel_modul
282 строки
11 KB
Batomunkuev Vladimir
1
01 фев 2025, 12:14
01 фев 2025, 12:14
0c256c5
Код
Авторство
О чём код?
### Конспект по Модулям Ядра на Языке Си с Добавлением IOCTL #### Введение Модули ядра — это отдельные части кода, которые можно динамически загружать и выгружать в ядро операционной системы без перезагрузки системы. Это позволяет расширять функциональность ядра и упрощает управление системой. В этом конспекте рассмотрим основы создания модулей ядра и добавления механизма IOCTL для взаимодействия с устройствами. ### Основные аспекты работы с модулями ядра 1. **Загрузка и выгрузка модулей** - Модули загружаются с помощью команды `insmod` или `modprobe`. - Выгружаются с помощью команды `rmmod`. 2. **Файлы модулей** - Исходный код модуля пишется на языке C. - Компилируется в объектный файл `.o` или бинарный файл `.ko`. 3. **Структура модуля** ```c #include <linux/module.h> // Необходимые для всех модулей заголовочные файлы #include <linux/kernel.h> #include <linux/init.h> // Макросы для описания имени и версии модуля MODULE_LICENSE("GPL"); MODULE_AUTHOR("Author Name"); MODULE_DESCRIPTION("A simple example Linux module."); MODULE_VERSION("0.1"); // Функция инициализации модуля static int __init hello_init(void) { printk(KERN_INFO "Hello world!\n"); return 0; // Возвращаем 0 при успешной инициализации } // Функция завершения работы модуля static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye world!\n"); } // Регистрация функций инициализации и завершения module_init(hello_init); module_exit(hello_exit); ``` ### Ключевые элементы модуля 1. **Функция инициализации (`__init`)** - Выполняется при загрузке модуля. - Должна возвращать 0 при успешном выполнении, иначе модуль не будет загружен. 2. **Функция завершения (`__exit`)** - Выполняется при выгрузке модуля. - Очищает ресурсы, закрывает соединения и т.д. 3. **Макросы для метаданных** - `MODULE_LICENSE`: Лицензия модуля (например, "GPL"). - `MODULE_AUTHOR`: Автор модуля. - `MODULE_DESCRIPTION`: Описание модуля. - `MODULE_VERSION`: Версия модуля. ### Пример: Создание простого модуля #### Шаг 1: Создание исходного файла Создайте файл `hello.c` с содержимым: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple Hello World Module"); MODULE_VERSION("0.1"); static int __init hello_init(void) { printk(KERN_INFO "Hello, kernel!\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye, kernel!\n"); } module_init(hello_init); module_exit(hello_exit); ``` #### Шаг 2: Создание Makefile Создайте файл `Makefile` рядом с исходным файлом: ```makefile obj-m += hello.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean ``` #### Шаг 3: Компиляция модуля Выполните команду `make` для компиляции модуля. Будет создан файл `hello.ko`. #### Шаг 4: Загрузка и выгрузка модуля - Загрузите модуль командой: `sudo insmod hello.ko` - Проверьте логи ядра: `dmesg | tail` - Выгрузите модуль командой: `sudo rmmod hello` ### Обработка ошибок и отладка 1. **Проверка ошибок** - Используйте `printk()` для вывода сообщений в журнал ядра. - Используйте макросы `pr_info()`, `pr_warn()`, `pr_err()` для более удобного вывода. 2. **Отладка** - Используйте `dmesg` для просмотра сообщений ядра. - Используйте `lsmod` для проверки загруженных модулей. - Используйте `modinfo` для получения информации о модуле. ### Введение в IOCTL IOCTL (Input/Output Control) — это механизм ядра Linux, который позволяет взаимодействовать с устройствами через системные вызовы. Он предоставляет способ отправки произвольных команд и данных к драйверам устройств. #### Основные аспекты работы с IOCTL 1. **Функция ioctl** - Вызывается из пользовательского пространства с помощью системного вызова `ioctl()`. - Обрабатывается функцией обработчика IOCTL в модуле ядра. 2. **Регистрация IOCTL в модуле** ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> // Для файловых операций #include <linux/uaccess.h> // Для copy_to_user и copy_from_user MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple example Linux module with IOCTL"); MODULE_VERSION("0.1"); #define DEVICE_NAME "example_device" #define IOCTL_MAGIC 'k' #define IOCTL_CMD _IOWR(IOCTL_MAGIC, 0, int) static int device_open(struct inode *inode, struct file *file) { printk(KERN_INFO "Device opened\n"); return 0; } static int device_release(struct inode *inode, struct file *file) { printk(KERN_INFO "Device released\n"); return 0; } static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int value; if (_IOC_TYPE(cmd) != IOCTL_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) >= 1) return -ENOTTY; switch (cmd) { case IOCTL_CMD: if (copy_from_user(&value, (int __user *)arg, sizeof(value))) { return -EFAULT; } printk(KERN_INFO "Received value: %d\n", value); value *= 2; // Пример обработки данных if (copy_to_user((int __user *)arg, &value, sizeof(value))) { return -EFAULT; } break; default: return -ENOTTY; } return 0; } static struct file_operations fops = { .owner = THIS_MODULE, .open = device_open, .release = device_release, .unlocked_ioctl = device_ioctl, }; static int major_number; static int __init hello_init(void) { major_number = register_chrdev(0, DEVICE_NAME, &fops); if (major_number < 0) { printk(KERN_ALERT "Failed to register a major number\n"); return major_number; } printk(KERN_INFO "Registered device with major number %d\n", major_number); return 0; } static void __exit hello_exit(void) { unregister_chrdev(major_number, DEVICE_NAME); printk(KERN_INFO "Unregistered device\n"); } module_init(hello_init); module_exit(hello_exit); ``` ### Ключевые элементы работы с IOCTL 1. **Определение IOCTL команды** - Используйте макросы `_IO`, `_IOR`, `_IOW`, `_IOWR` для создания команд. - Например, `_IOWR('k', 0, int)` создает команду для чтения и записи целого числа. 2. **Обработка IOCTL в модуле** - Реализация функции `device_ioctl`, которая принимает команду и аргументы от пользователя. - Проверка типа и номера команды для предотвращения несанкционированного доступа. 3. **Копирование данных между пользовательским и ядерным пространством** - Используйте `copy_from_user` для получения данных от пользователя. - Используйте `copy_to_user` для отправки данных пользователю. ### Пример использования IOCTL из пользовательского пространства Создайте файл `ioctl_test.c`: ```c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #define IOCTL_MAGIC 'k' #define IOCTL_CMD _IOWR(IOCTL_MAGIC, 0, int) int main() { int fd = open("/dev/example_device", O_RDWR); if (fd < 0) { perror("Failed to open device file"); return EXIT_FAILURE; } int value = 42; printf("Sending value: %d\n", value); if (ioctl(fd, IOCTL_CMD, &value) < 0) { perror("Failed to send ioctl command"); close(fd); return EXIT_FAILURE; } printf("Received value: %d\n", value); close(fd); return EXIT_SUCCESS; } ``` ### Компиляция и запуск теста 1. **Компиляция модуля** - Используйте Makefile из предыдущего примера для компиляции модуля. 2. **Загрузка модуля** - Загрузите модуль командой: `sudo insmod example_device.ko` - Убедитесь, что устройство создано: `ls /dev | grep example_device` 3. **Компиляция и запуск теста** - Компилируйте тестовый файл: `gcc -o ioctl_test ioctl_test.c` - Запустите тест: `sudo ./ioctl_test` ### Заключение Использование IOCTL позволяет реализовать гибкие механизмы взаимодействия между пользовательским пространством и ядром. Это мощный инструмент для управления устройствами и расширения функциональности системы. Понимание и правильная реализация IOCTL важны для разработчиков драйверов и модулей ядра. Работа с модулями ядра требует понимания особенностей ядра и его взаимодействия с пользовательскими процессами. Модули позволяют гибко управлять функциональностью системы, обеспечивая высокую производительность и стабильность.