раздражающее поведение select () в c - PullRequest
6 голосов
/ 13 ноября 2008
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-сокет получает данные. Мне кажется, это пустая трата ресурсов, чтобы каждый раз приходилось очищать и заполнять набор.

У кого-нибудь есть хорошее объяснение, почему это, а еще лучше, возможно, предложение, как этого избежать?

Ответы [ 3 ]

12 голосов
/ 13 ноября 2008

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 будет добавлен в набор, вы будете уведомлены о событиях на этом fd без необходимости постоянно передавать его снова.

7 голосов
/ 13 ноября 2008

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

Всегда инициализируйте fd_set прямо перед его использованием.

0 голосов
/ 13 ноября 2008

Так работает выбор. Это работает лучше всего, и имеет больше смысла, если у вас более одного сокета. Это своего рода пункт: вы выбираете через множество сокетов. Если вы хотите читать из одного сокета, просто прочитайте или запишите его.

...