Вход/Регистрация
UNIX: разработка сетевых приложений
вернуться

Стивенс Уильям Ричард

Шрифт:

Например, чтобы определить переменную типа

fd_set
и затем установить биты для дескрипторов 1, 4 и 5, мы пишем:

fd_set rset;

FD_ZERO(&rset); /* инициализируем набор все биты сброшены */

FD_SET(1, &rset); /* устанавливаем бит для fd 1 */

FD_SET(4, &rset); /* устанавливаем бит для fd 4 */

FD_SET(5, &rset); /* устанавливаем бит для fd 5 */

Важно инициализировать набор, так как если набор будет создан в виде автоматической переменной и не проинициализировав, результат может оказаться непредсказуемым.

Любой из трех средних аргументов функции

select
—
readset
,
writeset
или
exceptset
— может быть задан как пустой указатель, если нас не интересует определяемое им условие. На самом деле, если все три указателя пустые, мы просто получаем таймер большей точности, чем обычная функция Unix
sleep
(позволяющая задавать время с точностью до секунды). Функция
poll
обеспечивает аналогичную функциональность. На рис. С.9 и С.10 [110] показана функция
sleep_us
, реализованная с помощью функций
select
и
poll
, которая позволяет устанавливать время ожидания с точностью до микросекунд.

Аргумент

maxfdp1
задает число проверяемых дескрипторов. Его значение на единицу больше максимального номера проверяемого дескриптора (поэтому мы назвали его
maxfdp1
). Проверяются дескрипторы 0, 1, 2 и далее до
maxfdp1
– 1 включительно.

Константа

FD_SETSIZE
, определяемая при подключении заголовочного файла
<sys/select.h>
, является максимальным числом дескрипторов для типа данных
fd_set
. Ее значение часто равно 1024, но такое количество дескрипторов используется очень немногими программами. Аргумент
maxfdp1
заставляет нас вычислять наибольший интересующий нас дескриптор и затем сообщать ядру его значение. Например, в предыдущем коде, который включает дескрипторы 1, 4 и 5, значение аргумента
maxfdp1
равно 6. Причина, по которой это 6, а не 5, в том, что мы задаем количество дескрипторов, а не наибольшее значение, а нумерация дескрипторов начинается с нуля.

ПРИМЕЧАНИЕ

Зачем нужно было включать этот аргумент и вычислять его значение? Причина в том, что он повышает эффективность работы ядра. Хотя каждый набор типа fd_set может содержать множество дескрипторов (обычно до 1024), реальное количество дескрипторов, используемое типичным процессом, значительно меньше. Эффективность возрастает за счет того, что не копируются ненужные части набора дескрипторов между ядром и процессом и не требуется проверять биты, которые всегда являются нулевыми (см. раздел 16.13 [128]).

Функция

select
изменяет наборы дескрипторов, на которые указывают аргументы
readset
,
writeset
и
exceptset
. Эти три аргумента являются аргументами типа «значение-результат». Когда мы вызываем функцию, мы указываем интересующие нас дескрипторы, а по ее завершении результат показывает нам, какие дескрипторы готовы. Проверить определенный дескриптор из структуры
fd_set
после завершения вызова можно с помощью макроса
FD_ISSET
. Для дескриптора, не готового для чтения или записи, соответствующий бит в наборе дескрипторов будет сброшен. Поэтому мы устанавливаем все интересующие нас биты во всех наборах дескрипторов каждый раз, когда вызываем функцию
select
.

ПРИМЕЧАНИЕ

Две наиболее общих ошибки программирования при использовании функции select — это забыть добавить единицу к наибольшему номеру дескриптора и забыть, что наборы дескрипторов имеют тип «значение-результат». Вторая ошибка приводит к тому, что функция select вызывается с нулевым битом в наборе дескрипторов, когда мы думаем, что он установлен в единицу.

Возвращаемое этой функцией значение указывает общее число готовых дескрипторов во всех наборах дескрипторов. Если значение таймера истекает до того, как какой-нибудь из дескрипторов оказывается готов, возвращается нулевое значение. Возвращаемое значение -1 указывает на ошибку (которая может произойти, если, например, выполнение функции прервано перехваченным сигналом).

ПРИМЕЧАНИЕ

В ранних реализациях SVR4 функция select содержала ошибку: если один и тот же бит находился в нескольких наборах дескрипторов — допустим, дескриптор был готов и для чтения, и для записи, — он учитывался только один раз. В современных реализациях эта ошибка исправлена.

При каких условиях дескриптор становится готовым?

Мы говорили об ожидании готовности дескриптора для ввода-вывода (чтения или записи) или возникновения исключительной ситуации, требующей обработки (внеполосные данные). В то время как готовность к чтению и записи очевидна для файловых дескрипторов, в случае дескрипторов сокетов следует более внимательно изучить те условия, при которых функция

select
сообщает, что сокет готов (см. рис. 16.52 [128]).

1. Сокет готов для чтения, если выполнено хотя бы одно из следующих условий:

1) число байтов данных в приемном буфере сокета больше или равно текущему значению минимального количества данных (low water-mark) для приемного буфера сокета. Операция считывания данных из сокета не блокируется и возвратит значение, большее нуля (то есть данные, готовые для чтения). Мы можем задать значение минимального количества данных (low-water mark) с помощью параметра сокета

SO_RCVLOWAT
. По умолчанию для сокетов TCP и UDP это значение равно 1;

  • Читать дальше
  • 1
  • ...
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: