Как перебрать только активные файловые дескрипторы из результата fd_set из select ()? - PullRequest
6 голосов
/ 29 марта 2011

Итак, в моей текущей реализации сервера, это сейчас что-то вроде этого:

  void loop(){
     // step 1: clear set

     fd_set readfds;

     while(true){

        // step 1:
        FD_ZERO(readfds);

        // step 2:
        loop_through_sockets_and_add_active_sockets_to(theset);

        // step 3:
        switch(select(FD_SETSIZE, &readfds, 0, 0, &tv)) {
           case SOCKET_ERROR:
              patia->receiveEvent(Error, net::getError());
              return;
           case 0:
              return;
        }

        // step 4:
        loop through sockets and check, using FD_ISSET, 
        which read fd's have incoming data.

     }
  }

Теперь, не очищая fd_set (используя FD_SET, FD_CLR, когда каналы добавляются / удаляются только), был бы лучший способ сделать что-то.

Мой вопрос: как вы можете циклически проходить через fd_set после select (), не проверяя каждый элемент набора, если он является частью набора, без использования FD_ISSET?

Я имею в виду, что когда у вас 4000 активных подключений, при поступлении входящих данных вышеприведенный цикл должен пройти через потенциальные 4000 сокетов, прежде чем перейти к нужному. Сложность будет n ^ 2, если все потоки активны много!

Ответы [ 3 ]

6 голосов
/ 29 марта 2011

Мой вопрос: как вы можете циклически проходить через fd_set после select (), не проверяя каждый элемент набора, если он является частью набора, без использования FD_ISSET?

Вы не можете.

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

Я имею в виду, что когда у вас 4000 активных подключений, при поступлении входящих данных вышеприведенный цикл должен пройти через потенциальные 4000 сокетов, прежде чем перейти к нужному. Сложность будет n ^ 2, если все потоки активны много!

Я не понимаю, откуда вы взяли O (n ^ 2). Конечно, после возврата из select() вы пройдете через набор, обрабатывая каждый готовый дескриптор в пути. Если у вас есть 4000 готовых дескрипторов ввода-вывода, издержки циклического обхода массива из 4000 объектов в памяти C будут довольно незначительными.

4 голосов
/ 22 апреля 2013

Вы можете.
Это повторяет набор без ISSET() -ing всех FD из массива.Но вы все равно должны коснуться всех long с в массиве fd_set.__fds_bits.

#include<sys/select.h>
#include<stdio.h>
int main(void)
{
    fd_set fds;
    FD_ZERO(&fds);
    //Fill the set.
    FD_SET(6, &fds);FD_SET(20, &fds);FD_SET(33, &fds);FD_SET(200, &fds);
    int i;
    unsigned long *m = (unsigned long *)__FDS_BITS(&fds);
    int fd=0;
    for (i = 0; i < sizeof (fd_set) / sizeof (unsigned long); ++i) //can use int, long or long long. Using long because internal structure is long.
    {
        fd=sizeof (unsigned long)*i*8;
        while(m[i]!=0)
        {
            fd+=__builtin_ctzl(m[i]); //Get Number of trailing zero bits in long.
            printf("FD=%d\n",fd);
            /*Found FD*/
            m[i]>>=(__builtin_ctzl(m[i]))+1; 
            ++fd;
        }
    }
    return 0;
}

Это прекрасно работает для меня, используя gcc (SUSE Linux) 4.6.2


Фон

в моей системе fd_set выглядит следующим образом (извлечь и упростить /usr/include/sys/select.h):

typedef struct {
    __fd_mask __fds_bits[__FD_SETSIZE/__NFDBITS];
}

с __fd_mask, являющимся typedef для long int.
__FD_SETSIZE/__NFDBITS, похоже, 16 в моей системе.
Таким образом, этот массив составляет (__FD_SETSIZE/__NFDBITS)*sizeof(__fd_mask)*8 битов.
С __NFDBITS = 8*sizeof(__fd_mask) вы видите, что он содержит __FD_SETSIZE битов.

Просмотр определенных макросов в /usr/include/bits/select.h показывает, что __fd_bits используются для хранения fds.Когда задано значение fd n, бит nth в __fd_bits установлен равным 1.

i386 (среди других ) процессоры имеют одну операцию для подсчета завершающего нулябиты числа.С этим количеством конечных нулей вы можете легко сдвинуть запись __fd_bits на эту величину + 1, и вы найдете следующий true бит.

По сравнению с циклическим набором записей вашего fd_set вам нужнокак минимум __FD_SETSIZE/__NFDBITS=16 циклов, когда не выполняется оптимизация с использованием возвращаемого значения select.

Но убедитесь, что ваш процессор и компилятор поддерживают операцию для выбранного типа цикла.Если по умолчанию НЕ используется битовая операция, но сложная реализация, это может ухудшиться.

Если это на самом деле лучше, чем зацикливание, необходимо доказать известные fds.

3 голосов
/ 17 ноября 2011

Весьма вероятно, что у вас уже есть структура данных, связанная с каждым открытым дескриптором файла, который select() ed. Вы уже нуждаетесь в способе ссылки на это при демультиплексировании fd_set s, возвращаемых из select().

Если число файловых дескрипторов значительно меньше, чем FD_SETSIZE, вам, вероятно, лучше повторить эту процедуру (например, только открытые дескрипторы файлов) и использовать FD_ISSET() для проверки активности.

...