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 выяснить, когда время истекло */
53: printf("Вызовов poll в секунду: %d\n", count);
54:
55: return 0;
56: }
Здесь используется устройство
/dev/zero
, предоставляющее бесконечное количество нулей, что обеспечивает немедленный возврат системных вызовов. Значение
HIGH_FD
можно изменить, чтобы посмотреть, как деградирует
select
по мере роста значений файловых дескрипторов.
В определенной системе при не очень высоком значении
HIGH_FD
, равном 2, программа показала, что ядро за секунду может обрабатывать в четыре раза больше вызовов
poll
, чем вызовов
select
. При увеличении
HIGH_FD
до 1000 эффективность
poll
становится в 40 раз выше, чем у
select
.
13.1.5. Мультиплексирование с помощью
epoll
В версии 2.6 ядра Linux был предложен третий метод для мультиплексированного ввода-вывода по имени
epoll
. Будучи более сложным, чем
poll
или
select
,
epoll
ликвидирует узкие места, связанные с производительностью, которые характерны для обоих методов.
Оба системных вызова
poll
и
select
передают на проверку полный список файловых дескрипторов при каждом вызове. Каждый из этих дескрипторов должен быть обработан системным вызовом, даже если только один из них готов к чтению или записи. Когда проверяются десятки, сотни или тысячи файловых дескрипторов, эти системные вызовы превращаются в узкие места; ядро тратит много времени на выяснение того, какие именно файловые дескрипторы приложению необходимо проверить.
При использовании
epoll
приложения обеспечивают ядро списком файловых дескрипторов для проверки с помощью одного системного вызова, а затем для проверки этих дескрипторов с помощью другого системного вызова. После создания списка ядро постоянно проверяет эти дескрипторы для событий, интересующих приложение [79] , а затем сообщает о событии. Как только приложение запрашивает у ядра файловые дескрипторы, готовые для дальнейшей обработки, ядро предоставляет список без необходимости проверки каждого файлового дескриптора.
79
Фактически ядро устанавливает обратный вызов для каждого файла, а затем, когда происходит событие, активизирует обратный вызов. Этот механизм устраняет проблемы масштабируемости при очень большом количестве файловых дескрипторов, поскольку опрос не используется в каждой точке.