Выбрать главу

12:  fd_set watchset; /* fds для чтения */

13:  fd_set inset; /* обновляется select() */

14:

15:  /* открыть оба канала */

16:  if ((fds[0] = open("p1", O_RDONLY | O_NONBLOCK)) < 0) {

17:   perror("open p1");

18:   return 1;

19:  }

20:

21:  if ((fds[1] = open("p2", O_RDONLY | O_NONBLOCK)) < 0) {

22:   perror("open p2");

23:   return 1;

24:  }

25:

26:  /* начать чтение из обоих файловых дескрипторов */

27:  FD_ZERO(&watchset);

28:  FD_SET(fds[0], &watchset);

29:  FD_SET(fds[1], &watchset);

30:

31:  /* найти максимальный файловый дескриптор */

32:  maxfd = fds[0] > fds[1] ? fds[0] : fds[1];

33:

34:  /* пока наблюдаем за одним из fds[0] или fds[1] */

35:  while (FD_ISSET(fds[0], &watchset) ||

36:   FD_ISSET(fds[1], &watchset)) {

37:   /* здесь копируем watchset, потому что select() обновляет его */

38:   inset = watchset;

39:   if (select(maxfd + 1, &inset, NULL, NULL, NULL) < 0) {

40:    perror("select");

41:    return 1;

42:   }

43:

44:   /* проверить, какой из файловых дескрипторов

45:      готов для чтения из него */

46:   for (i = 0; i < 2; i++) {

47:    if (FD_ISSET(fds[i], &inset )) {

48:     /* fds[i] готов для чтения, двигаться дальше... */

49:     rc = read(fds[i], buf, sizeof (buf) - 1);

50:     if (rc < 0) {

51:      perror("read");

52:      return 1;

53:     } else if (!rc) {

54:      /* этот канал закрыт, не пытаться

55:         читать из него снова */

56:      close(fds[i]);

57:      FD_CLR(fds[i], &watchset);

58:     } else {

59:      buf[rc] = '\0';

60:      printf("чтение: %s", buf);

61:     }

62:    }

63:   }

64:  }

65:

66:  return 0;

67: }

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() в секунду: %d\n", count);

42:

43:  pollFds.fd = HIGH_FD;

44:  pollFds.events = POLLIN;

45:  count = 0;