Разработка модуля для ядра Linux 2.6.38
Курсовая работа, 03 Октября 2011, автор: пользователь скрыл имя
Краткое описание
Модуль - это некий код, который может быть загружен или выгружен ядром по мере необходимости[1]. Модули расширяют функциональные возможности ядра без необходимости перезагрузки системы. Например, одна из разновидностей модулей ядра, драйверы устройств, позволяют ядру взаимодействовать с аппаратурой компьютера. При отсутствии поддержки модулей пришлось бы писать монолитные ядра и добавлять новые возможности прямо в ядро. При этом, после добавления в ядро новых возможностей, пришлось бы перезагружать систему.
Оглавление
Задание 4
Особенности реализации модуля ядра 5
Методы 5
Вывод информации 5
Пространство пользователя и пространство ядра 5
Сборка 6
Загрузка и выгрузка ядра 6
Файлы символьных устройств 7
Структура file_operations 7
Регистрация устройства 8
Отключение устройства 8
Создание файла устройства 8
Работа пользовательского приложения с модулем ядра 9
Разработка модуля ядра 10
Разработка простейшего модуля ядра 10
Создание прототипов функций 10
Реализация функций 11
Организация обмена данных с пространством пользователя 11
Реализация политики безопасности 12
Организация работы с модулем ядра 14
Заключение 15
Библиографический список 16
Приложение 17
Структура file_operations для ядра Linux 2.6.38 17
Код модуля ядра 18
Файлы: 1 файл
Модуль ядра.docx
— 72.24 Кб (Скачать)- try_module_get(THIS_MODULE): увеличивает счетчик обращений на 1.
- try_module_put(THIS_MODULE): уменьшает счетчик обращений на 1.
Создание файла устройства
Для
обеспечения возможности
mknod char_dev c 100 0
Где char_dev – имя файла, а 100 – номер который возвращает функция register_chrdev() при подключении модуля.
Работа пользовательского приложения с модулем ядра
Для работы с модулем ядра необходимо подключиться к файлу устройства с помощью функции open. Далее работа с модулем ядра ничем не отличается от работы с обычным файлом, естественно, учитывая специфику модуля и реализованные функции.
Пример программы для работы с модулем:
- #include <fcntl.h> /* open */
- #include <unistd.h> /* exit */
- #include <sys/ioctl.h> /* ioctl */
- main()
- {
- int file_desc, ret_val, i = 0;
- char msg[] = "Hello there!\n";
- char msg2[] = "One two three\n";
- char bmsg[80];
- file_desc = open(DEVICE_FILE_
NAME , O_RDWR); - if (file_desc < 0) {
- printf("Error
opening %s\n", DEVICE_FILE_
NAME ); - exit(-1);
- }
- ioctl(file_desc, 0, 1);
- write(file_desc, msg, sizeof(m
sg )); - ioctl(file_desc, 0, 2);
- write(file_desc, msg2, sizeof(
msg2 )); - ioctl(file_desc, 0, 1);
- lseek(file_desc, 3, 0);
- ret_val = read(file_desc, bmsg
, sizeof(bmsg)); - bmsg[ret_val] = "\0";
- printf("-->");
- printf(bmsg);
- printf("\n");
- ioctl(file_desc, 0, i++);
- close(file_desc);
- }
Функция ioctl – определяет в какой буфер происходит запись. В 17 строке выбираем буфер под номером 1. В следующей строке происходит запись в выбранный буфер. Далее происходит запись в буфер под номером 2. Команда lseek передвигает указатель чтения на +3 байта. Функция read в итоге вернет «lo there!».
Разработка модуля ядра
Разработка модуля ядра проходит в несколько этапов:
- создание простейшего модуля ядра;
- создание прототипов необходимых функций;
- реализация функций;
- организация обмена данными с пространством пользователя;
- реализация политики безопасности модуля ядра;
- организация работы с модулем ядра
Разработка простейшего модуля ядра
Для
простейшего модуля ядра достаточно
реализовать функцию
- #include <linux/kernel.h>
- #include <linux/module.h>
- int init_module(void)
- {
- printk("<1>Module loaded");
- return 0;
- }
- void cleanup_module()
- {
- printk("<1>module removed");
- }
Данный
код является полностью работоспособным
и выводит сообщения при
Создание прототипов функций
Для модуля понадобятся функции: open, close, write, read, ioctl и lseek. Создадим прототипы этих функций. Аргументы и возвращаемые значения можно найти в struct file_operations.
- static int device_open(struct
inode *inode, struct file *file ) - static int device_release(stru
ct inode *inode, struct file *file ) - static ssize_t device_read(struct file *file,
char __user * buffer, size_t length, loff_t * offset) - static ssize_t device_write(struct file *file
, const char __user * buffer,size_t length , loff_t * offset) - int device_ioctl(struct inode
* inode, struct file *file, unsigned int ioctl_num, unsignedlong ioctl_param) - static loff_t device_llseek(struct file *fil
e , int loff_t)
Прототипы реализованы, но как можно заметить, их имена не совпадают и именами нужных нам функций, и даже если бы совпадали, их было бы невозможно вызвать. Для того чтобы иметь возможность вызывать описанные функции необходимо определить экземпляр структуры file_operations, где должны быть указаны ссылки на эти методы:
- struct file_operations Fops = {
- .read = device_read,
- .write = device_write,
- .unlocked_ioctl = device_ioctl
, - .open = device_open,
- .release = device_release,
- .llseek = device_llseek
- };
Следующий
шаг это регистрация
- register_chrdev(MAJOR_NUM, DEV
ICE_NAME , &Fops);
Теперь можно быть уверенным, что функции будут вызваны. Но при выгрузке модуля, но все равно останется зарегистрированным, что может привести к ошибке. Чтобы этого избежать, нужно вызвать метод unregister_chrdev(MAJOR_NUM, DEVICE_NAME) в методе cleanup_module.
Реализация функций
На данном этапе реализуется логика модуля ядра, пример организации которой подробно рассматривается в главе «Работа пользовательского приложения с модулем ядра». Но некоторые функции не будут работать правильно, пока не будет организована передача данных между пространством пользователя и ядра.
Организация обмена данных с пространством пользователя
Примером функций для которых необходим обмен данными между пространством пользователя и пространством ядра могут служить функции write и read:
- static ssize_t device_write(struct file *file
, -
const char __user * buffer,
size_t length , loff_t * offset) - {
- int i;
- printk("<1>device_write(%p,%s,
%d)" , file, buffer, length); - switch (current_buffer) {
- case 0:
- for (i = 0; i < length && i
< BUF_LEN; i++) - get_user(buf0[i], buffer + i);
- break;
- case 1:
- for (i = 0; i < length && i
< BUF_LEN; i++) - get_user(buf1[i], buffer + i);
- break;
- case 2:
- for (i = 0; i < length && i
< BUF_LEN; i++) - get_user(buf2[i], buffer + i);
- break;
- }
- init_current_buffer();
- return i;
- }
- static ssize_t device_read(struct file *file,
char __user * buffer, size_t length, loff_t * offset) - {
- int bytes_read = 0;
- printk("<1>device_read(%p,%p,%
d) \n", file, buffer, length); - if (*Message_Ptr == 0)
- return 0;
- while (length && *Message_Ptr)
{ - put_user(*(Message_Ptr++), buf
fer ++); - length--;
- bytes_read++;
- }
- return bytes_read;
- }