Как работает сокет 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
, который предлагает более эффективный интерфейс для ожидания файловых дескрипторов.