Если сервер работает и с TCP, и с UDP, обычно также используется мультиплексирование ввода-вывода. Такой пример мы приводим в разделе 8.15.
Если сервер обрабатывает несколько служб и, возможно, несколько протоколов (например, демон
inetd
, который описан в разделе 12.5), обычно используется мультиплексирование ввода-вывода.
Область применения мультиплексирования ввода-вывода не ограничивается только сетевым программированием. Любому нетривиальному приложению часто приходится использовать эту технологию.
6.2. Модели ввода-вывода
Прежде чем начать описание функций
select
и
poll
, мы должны вернуться назад и уяснить основные различия между пятью моделями ввода-вывода, доступными нам в Unix:
блокируемый ввод-вывод;
неблокируемый ввод-вывод;
мультиплексирование ввода-вывода (функции
select
и
poll
);
ввод-вывод, управляемый сигналом (сигнал
SIGIO
);
асинхронный ввод-вывод (функции POSIX
aio_
).
Возможно, вы захотите пропустить этот раздел при первом прочтении, а затем вернуться к нему по мере знакомства с различными моделями ввода-вывода, подробно рассматриваемыми в дальнейших главах.
Как вы увидите в примерах этого раздела, обычно различаются две фазы операции ввода:
1. Ожидание готовности данных.
2. Копирование данных от ядра процессу.
Первый шаг операции ввода на сокете обычно включает ожидание прихода данных по сети. Когда пакет приходит, он копируется в буфер внутри ядра. Второй шаг — копирование этих данных из буфера ядра в буфер приложения.
Модель блокируемого ввода-вывода
Наиболее распространенной моделью ввода-вывода является модель
Рис. 6.1. Модель блокируемого ввода-вывода
В этом примере вместо TCP мы используем UDP, поскольку в случае UDP признак готовности данных очень прост: получена вся дейтаграмма или нет. В случае TCP он становится сложнее, поскольку приходится учитывать дополнительные переменные, например минимальный объем данных в сокете (low water-mark).
В примерах этого раздела мы говорим о функции
recvfrom
как о системном вызове, поскольку делаем различие между нашим приложением и ядром. Вне зависимости от того, как реализована функция
recvfrom
(как системный вызов в ядре, происходящем от Беркли, или как функция, активизирующая системный вызов
getmsg
в ядре System V), она обычно выполняет переключение между работой в режиме приложения и работой в режиме ядра, за которым через определенный промежуток времени следует возвращение в режим приложения.
На рис. 6.1 процесс вызывает функцию
recvfrom
, и системный вызов не возвращает управление, пока дейтаграмма не придет и не будет скопирована в буфер приложения либо пока не произойдет ошибка. Наиболее типичная ошибка — это прерывание системного вызова сигналом, о чем рассказывалось в разделе 5.9. Процесс блокирован в течение всего времени с момента, когда он вызывает функцию
recvfrom
, до момента, когда эта функция завершается. Когда функция
recvfrom
выполняется нормально, наше приложение обрабатывает дейтаграмму.
Модель неблокируемого ввода-вывода
Когда мы определяем сокет как неблокируемый, мы тем самым сообщаем ядру следующее: «когда запрашиваемая нами операция ввода-вывода не может быть завершена без перевода процесса в состояние ожидания, следует не переводить процесс в состояние ожидания, а возвратить ошибку». Неблокируемый ввод-вывод мы описываем подробно в главе 16, а на рис. 6.2 лишь демонстрируем его свойства.
Рис. 6.2. Модель неблокируемого ввода-вывода
В первых трех случаях вызова функции
recvfrom
данных для возвращения нет, поэтому ядро немедленно возвращает ошибку
EWOULDBLOCK
. Когда мы в четвертый раз вызываем функцию
recvfrom
, дейтаграмма готова, поэтому она копируется в буфер приложения и функция
recvfrom
успешно завершается. Затем мы обрабатываем данные.
Такой процесс, когда приложение находится в цикле и вызывает функцию
recvfrom
на неблокируемом дескрипторе, называется
Модель мультиплексирования ввода-вывода
В случае
select
или
poll
, и блокирование происходит в одном из этих двух системных вызовов, а не в действительном системном вызове ввода-вывода. На рис. 6.3 обобщается модель мультиплексирования ввода-вывода.
Рис. 6.3. Модель мультиплексирования ввода-вывода