На рис. 6.6 сравнивается пять различных моделей ввода-вывода. Здесь видно главное отличие четырех первых моделей в первой фазе, поскольку вторая фаза у них одна и та же: процесс блокируется в вызове функции
recvfrom
на то время, пока данные копируются из ядра в буфер вызывающего процесса. Асинхронный ввод-вывод отличается от первых четырех моделей в обеих фазах.
Рис. 6.6. Сравнение моделей ввода-вывода
Сравнение синхронного и асинхронного ввода-вывода
POSIX дает следующие определения этих терминов:
Операция синхронного ввода-вывода блокирует запрашивающий процесс до тех пор, пока операция ввода-вывода не завершится.
Операция асинхронного ввода-вывода не вызывает блокирования запрашивающего процесса.
Используя эти определения, можно сказать, что первые четыре модели ввода- вывода — блокируемая, неблокируемая, модель мультиплексирования ввода-вывода и модель управляемого сигналом ввода-вывода — являются синхронными, поскольку фактическая операция ввода-вывода (функция
recvfrom
) блокирует процесс. Только модель асинхронного ввода-вывода соответствует определению асинхронного ввода-вывода.
6.3. Функция select
Эта функция позволяет процессу сообщить ядру, что необходимо подождать, пока не произойдет одно из некоторого множества событий, и вывести процесс из состояния ожидания, только когда произойдет одно или несколько таких событий или когда пройдет заданное количество времени.
Например, мы можем вызвать функцию
select
и сообщить ядру, что возвращать управление нужно только когда наступит любое из следующих событий:
любой дескриптор из набора {1, 4, 5} готов для чтения;
любой дескриптор из набора {2, 7} готов для записи;
любой дескриптор из набора {1, 4} вызывает исключение, требующее обработки;
истекает 10,2 с.
Таким образом, мы сообщаем ядру, какие дескрипторы нас интересуют (готовые для чтения, готовые для записи или требующие обработки исключения) и как долго нужно ждать. Интересующие нас дескрипторы не ограничиваются сокетами: любой дескриптор можно проверить с помощью функции
select
.
Беркли-реализации всегда допускали мультиплексирование ввода-вывода с любыми дескрипторами. Система SVR3 ограничивала мультиплексирование ввода-вывода дескрипторами, которые являлись устройствами STREAMS (см. главу 31), но это ограничение было снято в SVR4.
#include
#include
int select(int
fd_set *
Описание этой функции мы начнем с последнего аргумента, который сообщает ядру, сколько следует ждать, пока один из заданных дескрипторов не будет готов. Структура
timeval
задает число секунд и микросекунд:
struct timeval {
long tv_sec; /* секунды */
long tv_usec; /* микросекунды */
};
С помощью этого аргумента можно реализовать три сценария:
1. Ждать вечно: завершать работу, только когда один из заданных дескрипторов готов для ввода-вывода. Для этого нужно определить аргумент
timeout
как пустой указатель.
2. Ждать в течение определенного времени: завершение будет происходить, когда один из заданных дескрипторов готов для ввода-вывода, но период ожидания ограничивается количеством секунд и микросекунд, заданным в структуре
timeval
, на которую указывает аргумент
timeout
.
3. Не ждать вообще: завершение происходит сразу же после проверки дескрипторов. Это называется
timeout
должен указывать на структуру
timeval
, а значение таймера (число секунд и микросекунд, заданных этой структурой) должно быть нулевым.
Ожидание в первых двух случаях обычно прерывается, когда процесс перехватывает сигнал и возвращается из обработчика сигнала.