Linux: Полное руководство
Шрифт:
27.3.12. Неблокирующие операции
Некоторые функции для работы с сокетами блокируют программу в случае, если удаленный процесс не осуществил требуемую операцию. Примеры таких функций:
♦ accept;
♦ connect;
♦ read;
♦ write.
Блокирование процесса очень нежелательно, поскольку во время ожидания можно было бы заняться чем-нибудь другим: например, обработать информацию, поступившую с другого сокета. Вы можете объявить сокеты неблокирующими с помощью системного вызовы ioctl.
Особенности работы некоторых функций в неблокирующем режиме:
♦ функция accept сразу
♦ функция connect тоже завершает работу, но с другой ошибкой: EINPROGRESS;
♦ функции чтения (read, recv, recvfrom) возвращают -1 или 0, если нет считываемых данных.
Ясное дело, что в таком режиме нужно периодически проверять наличие данных — ведь теперь процесс не будет их ожидать: если их нет, то функции просто возвратят -1 или 0.
Пример создания неблокирующих сокетов приведен ниже:
Листинг 27.9. Использование системного вызова ioctl
Глава 28
Программирование ядра
Из главы 7 вы узнали, что драйверы устройств в Linux выполнены в виде модулей ядра, и познакомились с пакетом module-init-tools (он же modutils для ядер 2.4), содержащим утилиты для выполнения основных операций над модулями ядра. В этой главе я покажу, как создать собственный модуль, позволяющий расширить возможности ядра операционной системы.
28.1. Каркас модуля
Что будет делать ваш модуль, зависит от вас — это может быть драйвер устройства или просто небольшой модуль, дополняющий ядро нужной вам функцией.
Для начала напишем каркас модуля на языке С. Этот каркас можно будет скомпилировать, но в результате получится модуль, который не делает ничего. Он просто послужит вам основой для написания настоящих, серьезных модулей.
Листинг 28.1. Каркас модуля ядра Linux (module1.с)
Теперь разберемся, что означает каждая строчка кода нашего будущего модуля. Первые две строчки делают обыкновенную программу модулем ядра Linux. Это директивы препроцессора cpp, обязательные для каждого модуля. Если вы не укажете их, компилятор сгенерирует совсем не тот код, которого мы от него ожидали.
Третья строка подключает заголовочный файл module.h, в котором находятся все необходимые для создания модуля определения.
Функция init_module вызывается при загрузке модуля ядром. Если загрузка модуля прошла успешно, функция возвращает 0, в противном случае она должна возвратить любое другое значение — код ошибки.
Функция cleanup_module вызывается при удалении модуля. Тип возвращаемого ею значения не определен, поэтому проверить, успешно ли произошло удаление, сама функция не позволяет.
Названия
функций init_module и cleanup_module необязательны — вы можете назвать их по-другому, но для этого вам нужно использовать функции module_init и module_exit, которые определены в заголовочном файле init.h.Переделаем наш шаблон так, чтобы он не использовал стандартные имена функций init_module и cleanup_module:
Листинг 28.2. Шаблон модуля с переименованием стандартных функций (module2.c)
Функциям module_init и module_exit нужно передать имена функций, которые будут вызваны при инициализации и удалении модуля соответственно. Зачем это нужно? Так, для общего развития — чтобы вы знали, для чего используются эти функции. Ваш модуль не станет работать лучше, если вы переименуете стандартные функции инициализации и удаления.
Помните, для чего используется программа modinfo? Да, для получения информации о модуле. Давайте добавим эту информацию в наш модуль.
Листинг 28.3. Информация о модуле (module3.c)
Макросы
При необходимости модуль может выводить на консоль сообщения, например, о невозможности инициализации устройства. Выводимые модулем сообщения будут запротоколированы службой протоколирования ядра — демоном klogd. Выводить сообщения нужно не с помощью функции printf, а функцией printk. В этом случае сообщения не только окажутся на системной консоли, но и будут запротоколированы в файле
Сейчас мы напишем модуль, который будет выводить сообщения при загрузке и при удалении.
Листинг 28.4. Использование функции printk (module.с)