Разработка приложений в среде Linux. Второе издание
Шрифт:
Каждый системный вызов
struct epoll_event
сообщает программе полное состояние контролируемого файлового дескриптора. Элемент events
может иметь установленные флаги EPOLLIN
, EPOLLOUT
или EPOLLPRI
, а также два новых флага, которые описаны ниже. EPOLLERR | С файлом связано ожидающее состояние ошибки; это случается, если ошибка происходит в сокете, когда приложение не считывает из него или не записывает в него. |
EPOLLHUP | Файловый
|
На первый взгляд это все может показаться сложным, но на самом деле это очень похоже на работу
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: }
13.1.6 Сравнение
poll
и epoll
Методы
poll
и epoll
существенно отличаются; poll
хорошо стандартизован, но плохо масштабируется, в то время как epoll
существует только в Linux, но очень хорошо масштабируется. Приложения, наблюдающие за небольшим количеством файловых дескрипторов и переносимости величин, должны использовать poll
, но любому приложению, которому необходимо контролировать большое количество дескрипторов, лучше применять epoll
, даже если ему нужно поддерживать poll
для других платформ.
Поделиться с друзьями: