Осталось установить пакет kernel-source — исходные тексты ядра Linux. Кроме того, нужно убедиться, что ваше ядро поддерживает динамически загружаемые модули (п. 20.3.2.3). Если опция Enable loadable module support выключена
, ее нужно включить, сохранить файл конфигурации ядра и перекомпилировать ядро.Компилятор gcc нужно вызвать со множеством опций, поэтому для облегчения себе работы мы напишем make-файл (п. 21.2):
Листинг 28.5. Makefile для сборки модуля
CC=gcc
PATH=/usr/include /usr/src/linux-2.4/include
MODFLAGS:= -O3 -Wall -DLINUX -D__KERNEL__ -I$(PATH)
module.o: module.с
$(CC) $(MODFLAGS) -c module.с
Опции компилятора означают следующее:
♦ O3: будет использован третий уровень оптимизации (что это такое, вы узнаете в справочной системе gcc
:man gcc
);♦ Wall: включаем все предупреждения;
♦ DLINUX: генерируем код для Linux;
♦ I$(РАТН): определяем путь поиска заголовочных файлов. По умолчанию компилятор ищет файлы заголовков в каталоге /usr/include, но там может и не быть нужных файлов. Например, для дистрибутива ALT Linux (ядро 2.4.21) файлы заголовков находятся в каталоге /usr/include/linux-2.4.21rel-std-up.
Поместите make-файл в тот же каталог, где находится module.c, и выполните команду make
# insmod module.o
Вы увидите сообщение My module: Starting...
/var/log/messages
.28.3. Работа с устройствами
Мы только что написали модуль ядра, который можно установить, удалить, который выводит сообщения, но ничего полезного он не делает. Усложним нашу задачу: напишем модуль, управляющий некоторым устройством /dev/device
Перед тем, как приступить к написанию драйвера устройства, нужно поговорить о самих устройствах. Устройства бывают трех типов:
♦ Символьные: чтение и запись устройства выполняются посимвольно. Примером такого устройства может послужить последовательный порт.
♦ Блочные: чтение и запись устройства выполняются блоками, как правило, по 512 или 1024 байта. Самый яркий пример блочного устройства — жесткий диск.
♦ Сетевые: файлов этих устройств вы не найдете в каталоге /dev, поскольку использование файловой системы, то есть работа с этими устройствами как с файлами, неэффективно. Пример — сетевая карта (ethX).
Существуют устройства, которые нельзя отнести ни к одному типу, поскольку они не принимают и не передают данные. Пример: RTC (Real Time Clock).
Чтобы сделать устройство доступным для системы и пользовательских программ, нужно его зарегистрировать. В заголовочном файле fs.h
extern int register_chrdev(unsigned int, const char *,
struct file_operations *);
Первый аргумент — это старший номер (
Чтобы понять, для чего нужен старший номер, вспомним каталог /dev, содержащий файлы устройств. Файл /dev/tty1
При регистрации устройства нужно указать его тип — старший номер устройства. Но для этого нужно знать, какие номера свободны. Проще всего указать первым аргументом 0 — тогда функция возвратит первый свободный старший номер символьного устройства для вашей системы. Если старший номер указать явно, может возникнуть конфликт номеров, и наше устройство не будет зарегистрировано.
Второй параметр определяет имя устройства («device»). Последний параметр очень важен. Это структура указателей на функции для работы с нашим устройством. Наш модуль содержит таблицу доступных функций, а операционная система вызывает нужную функцию, когда пользовательской программе нужно выполнить операцию с файлом устройства (открытие/закрытие, чтение/запись). Таблица функций символьного устройства хранится в структуре file_operations, которая передается при регистрации устройства.