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

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

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

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

Ответ 1

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

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

Ответ 2

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

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