Первый макрос полностью очищает множество, следующие два макроса, соответственно, добавляют и удаляют файловый дескриптор. Мы использовали два макроса для ввода и два для вывода. Сначала мы полностью очистили множество, а потом добавили в него соответствующие дескрипторы:
FD_ZERO(&readv);
FD_ZERO(&writev);
FD_SET(sock, &readv);
FD_SET(sock, &writev);
Особого разговора требует последний параметр — тайм-аут. Тайм-аут можно задавать в секундах и миллисекундах. Например, следующие операторы объявляют тайм-аут длительностью 2 секунды и 5 миллисекунд:
struct timeval tout; /* тайм-аут для select */
tout.tv_sec = 2; /* 2 секунды */
tout.tv_usec = 5; /* 5 миллисекунд */
Если вы хотите не использовать тайм-аут (то есть ждать бесконечно), укажите NULL в качестве последнего аргумента.
Функция select() возвращает число файловых дескрипторов, на которых выполнились ожидаемые условия (ввод/вывод/ошибка) или -1 при ошибке.
Вот еще один пример использования функции select(). Мы будем ожидать ввода из файла и из сокета. Если будет достигнут тайм-аут в 20 секунд, пользователь увидит соответствующее сообщение; в противном случае он увидит сообщение: «Получен ввод из файла/сокета».
Листинг 27.8. Еще один пример использования select()
#include
#include
#include
#include
int k;
int sock;
int fd;
int max_fd;
fd_set input;
struct timeval timeout;
/* инициализация файла и сокета */
...
/* Инициализируем множество ввода */
FD_ZERO(input);
FD_SET(fd, input);
FD_SET(sock, input);
max_fd = (sock > fd ? sock : fd) + 1;
/* Задаем тайм-аут */
timeout.tv_sec = 20;
k = select(max_fd, &input, NULL, NULL, &timeout);
if (k < 0)
perror("Ошибка при вызове select");
else if (k == 0) puts("TIMEOUT");
else {
/* Получен ввод */
if (FD_ISSET(fd, input))
printf("Получен ввод из файла");
if (FD_ISSET(sock, input))
printf("Получен ввод из сокета");
}
Вроде бы код программы очень прост, но комментария заслуживает макрос FD_ISSET. С его помощью мы проверяем, есть ли во множестве ввода ввод из какого-либо источника.
27.3.12. Неблокирующие операции
Некоторые функции для работы с сокетами блокируют программу в случае, если удаленный процесс не осуществил требуемую операцию. Примеры таких функций:
♦ accept();
♦ connect();
♦ read();
♦ write().
Блокирование процесса очень нежелательно, поскольку во время ожидания можно было бы заняться чем-нибудь другим: например, обработать информацию, поступившую с другого сокета. Вы можете объявить сокеты неблокирующими с помощью системного вызовы ioctl().
Особенности работы некоторых функций в неблокирующем режиме:
♦ функция accept() сразу же завершает работу с ошибкой EWOULDBLOCK;
♦ функция connect() тоже завершает работу, но с другой ошибкой: EINPROGRESS;
♦ функции чтения (read(), recv(), recvfrom()) возвращают -1 или 0, если нет считываемых данных.
Ясное дело, что в таком режиме нужно периодически проверять наличие данных — ведь теперь процесс не будет их ожидать: если их нет, то функции просто возвратят -1 или 0.
Пример создания неблокирующих сокетов приведен ниже:
Листинг 27.9. Использование системного вызова ioctl()
#include "sock.h"
#include
void main() {
int sock;
int on = 1, off = 0; /* значение дня ioctl() */
/* Создаем неблокирующий сокет */
ioctl(sock, FIONBIO, &on);
}
Глава 28
Программирование ядра
Из главы 7 вы узнали, что драйверы устройств в Linux выполнены в виде модулей ядра, и познакомились с пакетом module-init-tools (он же modutils для ядер 2.4), содержащим утилиты для выполнения основных операций над модулями ядра. В этой главе я покажу, как создать собственный модуль, позволяющий расширить возможности ядра операционной системы.
28.1. Каркас модуля
Что будет делать ваш модуль, зависит от вас — это может быть драйвер устройства или просто небольшой модуль, дополняющий ядро нужной вам функцией.
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии