раздражающее поведение select() в c
Вопрос
while (xxx) {
timeout.tv_sec=TIMEOUT;
timeout.tv_usec=0;
FD_ZERO(&set);
FD_SET(sd,&set);
switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
xxxxx
}
работает нормально, однако
FD_ZERO(&set);
FD_SET(sd,&set);
while (xxx) {
timeout.tv_sec=TIMEOUT;
timeout.tv_usec=0;
switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
xxxxx
}
нет.Это работает в первый раз, но в следующий раз, когда он проходит через цикл while, он получает тайм-аут, даже если сокет SD получает данные.Мне кажется, что каждый раз очищать и заполнять набор — это пустая трата ресурсов.
У кого-нибудь есть хорошее объяснение, почему это так, а еще лучше, возможно, предложение, как этого избежать?
Решение
select изменяет свои аргументы.Вам действительно придется каждый раз заново инициализировать его.
Если вас беспокоят накладные расходы, то стоимость обработки полного FD_SET в ядре несколько более значительна, чем стоимость FD_ZERO.Вам нужно передать только максимальный fd, а не FD_SETSZIZE, чтобы минимизировать обработку ядра.В вашем примере:
switch (select((sd + 1),&set,NULL,NULL,&timeout))
В более сложном случае с несколькими fds вы обычно сохраняете переменную max:
FD_SET(sd,&set);
if (sd > max) max = sd;
... repeat many times...
switch (select((max + 1),&set,NULL,NULL,&timeout))
Если у вас будет большое количество файловых дескрипторов и вы обеспокоены накладными расходами на их перемещение, вам следует рассмотреть некоторые альтернативы select().Вы не указываете, какую ОС используете, но для Unix-подобных ОС их несколько:
- для Linux — epoll()
- для FreeBSD/NetBSD/OpenBSD/MacOS X, kqueue()
- для Solaris: /dev/poll
API-интерфейсы разные, но все они, по сути, представляют собой интерфейс ядра с отслеживанием состояния для поддержки набора активных описаний файлов.Как только файловый дескриптор будет добавлен в набор, вы будете получать уведомления о событиях на этом файловом диске без необходимости постоянно передавать его снова.
Другие советы
Прочтите справочную страницу выбора.Возвращаемый набор — это только файловые дескрипторы, готовые к использованию.Вы должны использовать FD_ISSET для проверки каждого из них, установлен он или нет.
Всегда инициализируйте fd_set непосредственно перед его использованием.
Именно так работает select.Лучше всего это работает и имеет больше смысла, если у вас более одного сокета.В этом и суть:вы выбираете множество сокетов.Если вы хотите читать из одного сокета, просто прочитайте или получите его.