Книга: Разработка приложений в среде Linux. Второе издание

13.1.4. Сравнение poll() и select()

13.1.4. Сравнение poll() и select()

Обладая одинаковой функциональностью, poll() и select() также имеют существенные отличия. Наиболее очевидным отличием является тайм-аут, поддерживающий миллисекундную точность для poll() и микросекундную точность для select(). В действительности же это отличие почти незначительно, поскольку ни один системный вызов не будет подготовлен с точностью до микросекунды.

Более важное отличие связано с производительностью. Интерфейс poll() обладает несколькими свойствами, делающими его намного эффективнее, чем select().

1. При использовании select() ядру необходимо проверить все файловые дескрипторы между 0 и numfds - 1, чтобы убедиться, заинтересовано ли приложение в событиях ввода-вывода для этого файлового дескриптора. Для приложений с большим количеством открытых файлов это может привести к существенным затратам, поскольку ядро проверяет, какие именно файловые дескрипторы являются объектом интереса.

2. Набор файловых дескрипторов передается ядру как битовая карта для select() и как список для poll(). Сложные битовые операции, необходимые для проверки и установки структур данных fd_set, менее эффективны, чем простые проверки, требуемые для struct pollfd.

3. Поскольку ядро переписывает структуры данных, передаваемые select(), приложение вынуждено сбрасывать эти структуры каждый раз перед вызовом select(). С poll() результаты ядра ограничены элементом revents, что устраняет потребность в восстановлении структур данных после каждого вызова.

4. Использование структуры, основанной на множествах (например, fd_set) не масштабируется по мере увеличения количества доступных процессу файловых дескрипторов. Поскольку ее размер статичен, а не динамичен (обратите внимание на отсутствие соответствующего макроса, например, FD_FREE), она не может расширяться или сжиматься в соответствии с потребностями приложения (или возможностями ядра). В Linux максимальный файловый дескриптор, который можно установить в fd_set, равен 1023. Если понадобится больший файловый дескриптор, select() работать не будет.

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

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

 1: /* select-vs-poll.с */
 2:
 3: #include <fcntl.h>
 4: #include <stdio.h>
 5: #include <sys/poll.h>
 6: #include <sys/select.h>
 7: #include <sys/signal.h>
 8: #include <unistd.h>
 9:
10: int gotAlarm;
11:
12: void catch(int sig) {
13:  gotAlarm = 1;
14: }
15:
16: #define HIGH_FD 1000
17:
18: int main(int argc, const char ** argv) {
19:  int devZero;
20:  int count;
21:  fd_set select Fds;
22:  struct pollfd pollFds;
23:
24:  devZero = open("/dev/zero", O_RDONLY);
25:  dup2(devZero, HIGH_FD);
26:
27:  /* с помощью signal выяснить, когда время истекло */
28:  signal(SIGALRM, catch);
29:
30:  gotAlarm =0;
31:  count = 0;
32:  alarm(1);
33:  while (!gotAlarm) {
34:   FD_ZERO(&selectFds);
35:   FD_SET(HIGH_FD, &selectFds);
36:
37:   select(HIGH_FD + 1, &selectFds, NULL, NULL, NULL);
38:   count++;
39:  }
40:
41:  printf("Вызовов select() в секунду: %dn", count);
42:
43:  pollFds.fd = HIGH_FD;
44:  pollFds.events = POLLIN;
45:  count = 0;
46:  gotAlarm = 0;
47:  alarm(1);
48:  while (!gotAlarm) {
49:   poll(&pollFds, 0, 0);
50:   count++;
51:  }
52:
53:  printf("Вызовов poll() в секунду: %dn", count);
54:
55:  return 0;
56: }

Здесь используется устройство /dev/zero, предоставляющее бесконечное количество нулей, что обеспечивает немедленный возврат системных вызовов. Значение HIGH_FD можно изменить, чтобы посмотреть, как деградирует select() по мере роста значений файловых дескрипторов.

В определенной системе при не очень высоком значении HIGH_FD, равном 2, программа показала, что ядро за секунду может обрабатывать в четыре раза больше вызовов poll(), чем вызовов select(). При увеличении HIGH_FD до 1000 эффективность poll() становится в 40 раз выше, чем у select().

Оглавление книги


Генерация: 1.491. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз