====== Задание по написанию модуля ядра ======
Задание является факультативным. Его исполнение не является обязательным для успешной сдачи экзамена.
Успешная сдача задания освобождает студента от сдачи экзамена.
=== Формулировка задания ===
Реализовать модуль ядра, который регистрирует символьное устройство с именем "math".
Устройство должно предоставлять возможность взаимодействия с ним с помощью системного вызова ioctl(int fd, unsigned int cmd, unsigned long arg).
Первый аргумент — файловый дескриптор, второй — номер команды, третий — адрес области памяти в пространстве пользователя (для иных устройств это произвольный аргумент).
Область памяти должна содержать массив элементов типа int.
Обработчик каждой команды должен считывать из адресного пространства пользователя аргумент(-ы), вычислять результат и записывать его в область памяти, следующую за последним аргументом.
К примеру, MATH_IOCTL_SUM считывает аргументы по адресам arg и arg+sizeof(int), а записывает результат по адресу arg+2*sizeof(int).
Это устройство должно отвечать на несколько команд системного вызова ioctl(2):
* MATH_IOCTL_SQR — возводит в квадрат единственный аргумент
* MATH_IOCTL_NEG — меняет знак единственного аргумента
* MATH_IOCTL_ADD — складывает два аргумента
* MATH_IOCTL_SUB — вычитает второй аргумент из первого
* MATH_IOCTL_MUL — умножает два аргумента
* MATH_IOCTL_DIV — делит первый аргумент на второй
=== Требования к модулю ===
* Модуль должен быть выгружаемым.
* Модуль должен использовать определения команд MATH_IOCTL_XXX из файла math.h.
* Модуль должен предоставлять возможность одновременной работы с символьным устройством 4 пользователям. Пятому пользователю (и последующим) на попытку открыть файл должен возвращаться код ошибки EBUSY.
* Одновременное использование символьного устройства не должно приводить к гонкам (race condition).
* Обработчик ioctl должен проверять входные данные и предотвращать целочисленные переполнения, деление на ноль и другие ошибки. Если аргументы пользователя некорректны, ему необходимо вернуть код ошибки EINVAL.
* Для взаимодействия с памятью пользователя необходимо использовать функции из набора get_user, put_user, copy_to_user, copy_from_user.
=== Материалы к заданию ===
* Исходный код утилиты math_ctl, с помощью которой можно проверить работоспособность символьного устройства.
* Makefile для сборки модуля и утилиты math_ctl. Для сборки выполните команду "make" без аргументов, для загрузки модуля выполните "make insert_module".
* Заготовка исходного файла модуля с указанием стандартных заголовочных файлов.
* Заголовочный файл с определением констант MATH_IOCTL_XXX.
Ссылка на архив: [[ftp://dione.intelib.org/pub/segoon/math.tar.gz]]
=== Советы ===
* Для упрощения отладки используйте pr_info(), печатающую отладочную информацию в системный лог (либо другие функции из семейства printk). Лог можно прочитать с помощью консольной команды dmesg.
* Для работы с символьным устройством используйте функции [[https://github.com/torvalds/linux/blob/v3.8/include/linux/fs.h#L2122|register_chrdev()]] и [[https://github.com/torvalds/linux/blob/v3.8/include/linux/fs.h#L2128|unregister_chrdev()]]. Из файловых операций структуры [[https://github.com/torvalds/linux/blob/v3.8/include/linux/fs.h#L1515|file_operations]] необходимо реализовать лишь методы open (вызывается при попытке открытия файла), release (вызывается при закрытии файлового дескриптора), unlocked_ioctl (вызывается при соответствующем системном вызове ioctl(2)). Остальные поля структуры file_operations для задания не важны.
* Для ограничения числа пользователей рекомендуется использовать атомарные переменные.
* Для сборки модуля требуются заголовочные файлы ядра. В дистрибутивах линукс семейства Debian (Debian, Ubuntu, Mint, ...) для их установки требуется пакет linux-headers-generic. В дистрибутивах семейства Fedora требуется пакет kernel-devel.
* Помните, что код ядра должен возвращать код ошибки в виде отрицательного значения для отличия от успешного (неотрицательного) результата. Пользовательский код преобразует это отрицательное значение в первоначальный код ошибки. Например, если ядро хочет вернуть код ошибки EBUSY, то соответствующая команда должна быть "return -EBUSY", а не "return EBUSY".
* Если не понятно, как реализовать ту или иную команду, посмотрите в код утилиты math_ctl. Возможно, по коду пользователя этой команды вы поймёте, как нужно действовать.
* Учтите, что адрес переменной типа int в адресном пространстве процесса имеет тип "int __user *", а не "int *". Макрос "__user" используется для нахождения небезопасного разыменования пользовательского указателя.
=== Сдача решений ===
Решённое задание отправляйте на почту segooon AT gmail.com.
При возникновении проблем компиляции, отладки или других проблем пишите на тот же адрес.
====== Второе задание по написанию модуля ядра ======
Задание является факультативным. Его сдача не влияет ни на сдачу экзамена, ни на получение автомата.
=== Формулировка задания ===
Реализовать модуль ядра, который регистрирует символьное устройство с именем "prime".
Устройство должно предоставлять возможность взаимодействия с помощью системного вызова read.
При чтении с помощью системного вызова read устройство выдаёт последовательность простых чисел типа uint64_t.
Устройство сохраняет информацию о том, сколько чисел было считано и при последовательных вызовах read выдаёт продолжение прерванной последовательности.
Вызов read с размером буфера, не кратным 8 (sizeof(uint64_t)), обрабатывается как ошибочный.
Возможно использование устройства более чем одним пользователем.
При этом каждый пользователь считывает свою последовательность простых чисел независимо от других пользователей.
Работа с очень большими числами не должна значительно задерживать обработку сигналов.
При получении сигнала процесс не должен блокироваться на длительное время.