Как работает выбор сокета()?

Как описано в книгах по сетевому программированию, функция select() отслеживает набор файловых дескрипторов для чтения. Например, вот часть кода:

select(numfds, &read_fds, NULL, NULL, NULL);

Здесь numfds - максимальное количество сокетов в read_fds + 1. Означает ли это, что каждый "мониторный" цикл select() просматривает все файловые дескрипторы процесса от 0 до numfds? Я имею в виду, если у меня есть только два файловых дескриптора для мониторинга (0 и 26), будет ли select отслеживать все дескрипторы от 0 до 26?


person Tural Gurbanov    schedule 04.02.2013    source источник


Ответы (2)


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

Да, fd в FD_SET 0-26 проверяются или контролируются. Это просто для того, чтобы установить верхнюю границу поиска файловых дескрипторов. IIRC это связано с тем, что тип fd_set реализован внутри как набор битов, поскольку легче указывать индексы, поскольку это может сэкономить место. Я могу ошибаться в предыдущем утверждении, так как я давно не посещал этот код в glibc.

person Jesus Ramos    schedule 04.02.2013

select выбирает, какие fd смотреть, основываясь на наборах fd, которые вы передаете (readfds, writefds, exceptfds). Наборы обычно реализуются как битовые векторы, поэтому select будет сканировать вектор, чтобы увидеть, какие fds выбраны. В качестве оптимизации вы передаете количество fd для сканирования, чтобы select не приходилось просматривать все fd до FD_SETSIZE (что может даже не совпадать в разных единицах компиляции).

select — довольно дорогой вызов из-за сканирования и необходимости сброса наборов после каждого вызова select. На многих платформах select просто реализуется поверх системного вызова poll, который предлагает более эффективный интерфейс для ожидания файловых дескрипторов.

person nneonneo    schedule 04.02.2013
comment
Да, но сканирует ли он все сокеты от 0 до numfds? - person Tural Gurbanov; 05.02.2013
comment
Почему вызывающая сторона несет ответственность за добавление одного? - person dreamlax; 05.02.2013
comment
@dreamlax по той же причине, по которой c-строки заканчиваются на \0, потому что кто-то так сказал, и с тех пор так и было. - person Jesus Ramos; 05.02.2013
comment
@TuralGurbanov: Да, он будет сканировать все от 0 до numfds. Используйте poll, если вы хотите избежать этого (бонус: poll не изменяет входной массив, поэтому вам не нужно повторно добавлять fds в каждом цикле). - person nneonneo; 05.02.2013
comment
Потому что мы начинаем наш счет с 0. Итак, если наш самый высокий fd сокета равен 26, это означает, что мы должны смотреть дескрипторы от 0 до 26, и это равно highest_socket + 1 - person Tural Gurbanov; 05.02.2013