Автор: Пользователь скрыл имя, 03 Октября 2011 в 19:49, курсовая работа
Модуль - это некий код, который может быть загружен или выгружен ядром по мере необходимости[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
Федеральное агентство по образованию
Государственное образовательное учреждение
высшего профессионального образования
«Омский государственный
технический университет»
Курсовая работа
по дисциплине:
“Организация и функционирование ОС”
Тема:
“Разработка модуля
для ядра Linux 2.6.38”.
Выполнил студ. гр. ИВМ-510
____________ Гончаров С.А.
Руководитель к.т.н.
___________
Флоренцев А.Н.
Омск 2010
Оглавление
Задание 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
Введение
В данной работе рассмотрен процесс создания модуля для ядра Linux 2.6.38, а так же методы взаимодействия пользовательских приложений с данным модулем.
Модуль - это некий код, который может быть загружен или выгружен ядром по мере необходимости[1]. Модули расширяют функциональные возможности ядра без необходимости перезагрузки системы. Например, одна из разновидностей модулей ядра, драйверы устройств, позволяют ядру взаимодействовать с аппаратурой компьютера. При отсутствии поддержки модулей пришлось бы писать монолитные ядра и добавлять новые возможности прямо в ядро. При этом, после добавления в ядро новых возможностей, пришлось бы перезагружать систему.
Разработать модуль для ядра Linux с возможностями записи и чтения для трех буферов, предусмотреть возможность выбора буфера и смещения указателя.
Для работы с модулем ядра будет создан символьный специальный файл с помощью утилиты mknod[2].
Для работы с модулем ядра должны быть реализованы следующие функции:
Любой модуль ядра должен иметь по меньшей мере две функции: функцию инициализации модуля - init_module(), которую вызывает insmod во время загрузки модуля, и функцию завершения работы модуля - cleanup_module(), которую вызывает rmmod. Этим функциям можно дать произвольные имена, но в этом случае нужно будет указать, что они используются при загрузке и выгрузке модуля с помощью макроопределений module_init(<Имя метода>), module_exit(<Имя метода. Для корректной работы модуля необходимо подключить заголовочный файл linux/module.h.
В модулях ядра нет возможности выводить информацию стандартными средствами. Для вывода информации используется метод printk().Основное назначение этой функции - дать ядру механизм регистрации событий и предупреждений. Поэтому, каждый вызов printk() сопровождается указанием приоритета, например <1> или KERN_ALERT. Всего в ядре определено 8 различных уровней приоритета для функции printk().
За доступ к ресурсам системы отвечает ядро, будь то видеоплата, жесткий диск или даже память. Программы часто конкурируют между собой за доступ к тем или иным ресурсам. Ядро должно обслужить конкурирующие запросы, и "выстроить" их в порядке очередности. К тому же сам центральный процессор может работать в различных режимах. Каждый из режимов имеет свою степень "свободы" действий. Микропроцессор Intel 80386 имеет четыре таких режима, которые часто называют "кольцами". Unix использует только два из них: наивысший (нулевое кольцо, известное так же под названием привилегированный режим) и низший (пользовательский режим)[3].
Как правило, программа обращается к библиотечным функциям, находясь в пользовательском режиме. Затем библиотечные функции обращаются к системным вызовам. Системные вызовы выступают от имени библиотечных функций, но работают в привилегированном режиме, так как они являются непосредственной частью ядра. Как только системный вызов завершает свою работу, он возвращает управление библиотечной функции и происходит обратный переход в пользовательский режим.
Обычно, о режимах исполнения, мы говорим как о пространстве ядра и пространстве пользователя. Эти два понятия охватывают не только два режима исполнения, но так же и то, что каждый из режимов имеет свое собственное отображение памяти - свое собственное адресное пространство.
Unix производит переключение из пространства пользователя в пространство ядра всякий раз, когда приложение делает системный вызов или приостанавливается аппаратным прерыванием. Код ядра, исполняющий системный вызов, работает в контексте процесса - от имени вызвавшего процесса и имеет доступ к данным в адресном пространстве процесса.
Основное назначение модулей - расширение функциональности ядра. Код модуля исполняется в пространстве ядра. Обычно модуль реализует обе, рассмотренные выше задачи - одни функции выполняются как часть системных вызовов, другие - производят обработку прерываний.
Для передачи данных из пространства ядра в пространство пользователя и наоборот используются функции put_user() и get_user().
Для сборки модуля необходимо создать make файл который будет содержать:
obj-m += имя_модуля.o
Так же создадим скрипт для компиляции:
make -C /usr/src/linux-headers-`uname -r` SUBDIRS=$PWD modules
Скрипт автоматически формирует строку с указанием расположения текущей версии ядра.
После успешной компиляции в папке должен появиться файл с расширением .ko(kernel object) это и есть модуль ядра.
Для подключения модуля ядра используется утилита insmod. Стоит учесть что подключить модуль можно только с правами администратора. Для автоматического подключения создадим скрипт:
sudo su
insmod имя_модуля.ko
Для выгрузки модуля ядра используется утилита rmmod пример использования:
rmmod имя_модуля.ko
Структура file_operations определена в файле linux/fs.h и содержит указатели на функции драйвера, которые отвечают за выполнение различных операций с устройством. Например, практически любой драйвер символьного устройства реализует функцию чтения данных из устройства. Адрес этой функции, хранится в структуре file_operations. Разные версии ядра имеют разные структуры file_operations, в приложении приведена структура для ядра Linux 2.6.38.8.
Драйвер зачастую реализует далеко не все функции, предусмотренные данной структурой. Например, драйвер, который обслуживает видеоплату, не обязан выполнять операцию чтения каталога (readdir). Поля структуры, соответствующие нереализованным функциям, заполняются "пустыми" указателями - NULL.
Компилятор gcc предоставляет программисту довольно удобный способ заполнения полей структуры в исходном тексте. Поэтому, если вы встретите подобный прием в современных драйверах, пусть это вас не удивляет. Ниже приводится пример подобного заполнения:
struct file_operations fops = {
read: device_read,
release: device_release};
Однако, существует еще один способ заполнения структур, который описывается стандартом C99[4]. Причем этот способ более предпочтителен. Cледует придерживаться этого синтаксиса, чтобы обеспечить переносимость драйверу:
struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
Доступ к символьным устройствам осуществляется посредством файлов устройств, которые как правило располагаются в каталоге /dev. Старший номер устройства говорит о том, какой драйвер с каким файлом устройства связан. Младший номер используется самим драйвером для идентификации устройства, если он обслуживает несколько таких устройств.
Добавление драйвера в систему подразумевает его регистрацию в ядре. Это означает - получение старшего номера в момент инициализации модуля. Получить его можно вызовом функции register_chrdev(), определенной в файле linux/fs.h.
Если файл устройства удалить после того как он будет открыт процессом, то может возникнуть ситуация когда процесс попытается обратиться к выгруженному драйверу. В результате произойдет попытка обращения к тому участку памяти, где ранее находилась функция обработки запроса. Если вам повезет, то этот участок памяти окажется не затертым ядром и вы получите сообщение об ошибке. Если не повезет - то произойдет переход в середину "чужой" функции. Результат такого "вызова" трудно предугадать заранее
Обычно,
если какая-то операция должна быть отвергнута,
функция возвращает код ошибки (отрицательное
число). В случае с функцией cleanup_module()