====== Задание по написанию модуля ядра ====== Задание является факультативным. Его исполнение не является обязательным для успешной сдачи экзамена. Успешная сдача задания освобождает студента от сдачи экзамена. === Формулировка задания === Реализовать модуль ядра, который регистрирует символьное устройство с именем "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)), обрабатывается как ошибочный. Возможно использование устройства более чем одним пользователем. При этом каждый пользователь считывает свою последовательность простых чисел независимо от других пользователей. Работа с очень большими числами не должна значительно задерживать обработку сигналов. При получении сигнала процесс не должен блокироваться на длительное время.