Два промежуточных параметра определяют буфер для ядра, в который можно копировать структуры 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
4: #include
5: #include
6: #include
7: #include
8:
9: #include
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: }
13.1.6 Сравнение poll()
и epoll
Методы poll()
и epoll
существенно отличаются; poll()
хорошо стандартизован, но плохо масштабируется, в то время как epoll
существует только в Linux, но очень хорошо масштабируется. Приложения, наблюдающие за небольшим количеством файловых дескрипторов и переносимости величин, должны использовать poll()
, но любому приложению, которому необходимо контролировать большое количество дескрипторов, лучше применять epoll
, даже если ему нужно поддерживать poll()
для других платформ.
Читать дальше