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

Последним системным вызовом epoll является epoll_wait(), который блокирует до тех пор, пока один или несколько контролируемых файловых дескрипторов не будут иметь данные для чтения или же не будут готовы к записи. Первым аргументом является дескриптор epoll, а последний — тайм-аутом в секундах. Если файловые дескрипторы не готовы к обработке до истечения тайм-аута, epoll_wait() возвращает 0.

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

Каждый системный вызов struct epoll_event сообщает программе полное состояние контролируемого файлового дескриптора. Элемент events может иметь установленные флаги EPOLLIN, EPOLLOUT или EPOLLPRI, а также два новых флага, которые описаны ниже.

EPOLLERR С файлом связано ожидающее состояние ошибки; это случается, если ошибка происходит в сокете, когда приложение не считывает из него или не записывает в него.
EPOLLHUP Файловый дескриптор завис; в главе 10 дана информация о том, когда это обычно происходит.

На первый взгляд это все может показаться сложным, но на самом деле это очень похоже на работу poll(). Вызов epoll_create() — это то же, что и распределение массива struct pollfd, a epoll_ctl() — это то же, что и инициализация элементов этого массива. Главный цикл, обрабатывающий файловые дескрипторы, использует epoll_wait() вместо системного вызова poll(), а close() аналогичен освобождению памяти, занимаемой массивом struct pollfd. Эти параллели помогают переписывать с применением epoll программы мультиплексирования, которые изначально были реализованы с помощью poll() или select().

Интерфейс epoll предлагает еще одну возможность, которую невозможно сравнить с poll() или select(). Поскольку дескриптор epoll в действительности является файловым дескриптором (вот почему его можно передавать close()), имеется возможность контролировать дескриптор epoll как часть еще одного дескриптора epoll либо через poll() или select(). Дескриптор epoll будет готов к чтению из любого места, а вызов epoll_wait() вернет события.

В окончательном решении проблемы мультиплексирования каналов, предложенном в данном разделе, используется epoll. Оно очень похоже на другие примеры, вот только определенная часть кода инициализации перемещена в новую функцию addEvent() для предотвращения нежелательного удлинения программы.

 1: /* mpx-epoll.c */

 2:

 3: #include <fcntl.h>

 4: #include <stdio.h>

 5: #include <stdlib.h>

 6: #include <sys/epoll.h>

 7: #include <unistd.h>

 8:

 9: #include <sys/poll.h>

10:

11: void addEvent(int epfd, char * filename) {

12:  int fd;

13:  struct epoll_event event;

14:

15:  if ((fd = open (filename, O_RDONLY | O_NONBLOCK)) < 0) {

16:   perror("open");

17:   exit(1);

18:  }

19:

20:  event.events = EPOLLIN;

21:  event.data.fd = fd;

22:

23:  if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event)) {

24:   perror("epoll_ctl(ADD)");

25:   exit(1);

26:  }

27: }

28:

29: int main(void) {

30:  char buf[4096];

31:  int i, rc;

32:  int epfd;

33:  struct epoll_event events[2];

34:  int num;

35:  int numFds;

36:

37:  epfd = epoll_create(2);

38:  if (epfd < 0) {

39:   perror("epoll_create");

40:   return 1;

41:  }

42:

43:  /* открыть оба канала и добавить их в набор epoll */

44:  addEvent(epfd, "p1");

45:  addEvent(epfd, "p2");

46:

47:  /* продолжать, пока есть один или более файловых дескрипторов

48:     для слежения */

49:  numFds = 2;

50:  while (numFds) {

51:   if ((num = epoll_wait(epfd, events,

52:    sizeof(events) / sizeof(* events),

53:    -1)) <= 0) {

54:   perror("epoll_wait");

55:   return 1;

56:  }

57:

58:  for (i = 0; i < num; i++) {

59:   /* events[i].data.fd готов для чтения */

60:

61:   rc = read(events[i].data.fd, buf, sizeof(buf) - 1);

62:   if (rc < 0) {

63:    perror("read");

64:    return 1;

65:   } else if (!rc) {

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

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

68:    if (epoll_ctl(epfd, EPOLL_CTL_DEL,

69:     events[i].data.fd, &events[i])) {

70:     perror("epoll_ctl (DEL)");

71:     return 1;

72:    }

73:

74:    close(events[i].data.fd);

75:

76:    numFds--;

77:   } else {

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

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

80:

81:   }

82:  }

83:

84:  close(epfd);

85:

86:  return 0;

87: }