epoll подключается к описателю файла, созданного epoll_create(2). Интерес к соответствующим описателям файлов затем регистрируется через epoll_ctl(2). Наконец, фактическое ожидание запускается epoll_wait(2).
Если описатель файла RFD добавлен к интерфейсу epoll, используя флаг EPOLLET, то вызов к epoll_wait(2), законченный на шаге 5, повиснет из-за возможных данных, все еще находящихся в буфере ввода. Причиной этого является то, что Edge Triggered доставляет события только если статус отслеживаемого устройства изменяеся с недоступного пространства ввода/вывода ( состояние 0) на доступное пространство ввода/вывода ( состояние 1). В приведенном выше примере событие на RFD будет сгенерировано ( предполагая, что считывающий буфер перенаправления был до этого пуст ) из-за записи, сделанной в 2, и события, предполагаемого в 3. Так-как операция чтения, сделанная в 4, не предполагает полного буфера данных ( то есть условие наличия остатка доступного пространства ввода/вывода )that is, the condition remains I/O space available ) и передача 0 -> 1 не может случиться в 5. Интерфейс epoll при использовании с флагом EPOLLET ( Edge Triggered ) должно использовать неблокированные описатели файлов для избежания блокировок чтения или записи для задания, обрабатывающего множество описателей файлов. Предлагается использовать далее epoll, как интерфейс Edge Triggered ( EPOLLET) и возможные методы обхода проблем.
Напротив, при использовании интерфейса Level Triggered, epoll по всем показателям лучше и быстрее poll(2), и может быть использован в любом случае, так как разделяет ту же семантику.
При использовании epoll с интерфейсом Level Triggered имеет ту же семантику poll(2), а Edge Triggered требует больше четкости для избежания зависаний в циклических приложениях. В этом примере слушающим является не блокированный сокет, для которого было вызвано listen(2). Функция do_use_fd() использует новые готовые описатели файлов до тех пор, пока не возвратится EAGAIN от другого read(2) или write(2). Машина приложения, с переданным статусом события, должна плсде получения EAGAIN записать свое текущее состояние так, чтобы последующий вызов do_use_fd() продолжил read(2) или write(2) с места остановки.
struct epoll_event ev, *events; for(;;) { nfds = epoll_wait(kdpfd, events, maxevents, -1); for(n = 0; n < nfds; ++n) { if(events[n].data.fd == listener) { client = accept(listener, (struct sockaddr *) &local, &addrlen); if(client < 0){ perror("accept"); continue; } setnonblocking(client); ev.events = EPOLLIN | EPOLLET; ev.data.fd = client; if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) { fprintf(stderr, "epoll set insertion error: fd=%d, client); return -1; } } else do_use_fd(events[n].data.fd); } }
При использовании в виде интерфейса Edge Triggered для качества работы возможно добавлять описатель файла внутрь интерфейса epoll ( EPOLL_CTL_ADD), указывая ( EPOLLIN | EPOLLOUT). Это позволит вам избежать постоянного переключения между EPOLLIN и EPOLLOUT вызывающими epoll_ctl(2) и EPOLL_CTL_MOD.
Возможно, что во время чтения (предположим, что вы читаете в цикле ожидания EAGAIN), больше вводов/выводов пришло на второе событие. Этот ввод/вывод будет считан немедленно. Однако, в следующий раз, когда вы вызовете epoll_wait(2) на этот fd, вам сообщится, что уже есть готовое событие, хотя ввод/вывод для него уже был сделан.
В случае с неблокированными описателями файлов это приведет к тому, что следуюющий вызов чтения немедленно возвратит EAGAIN. В случае блокировки описателей файлов вы зависните, ожидая чтения ввода/вывода, которого нет. Автор не рекомендует использовать блокировку описателей файлов вместе с поведением Edge Triggered, но и останавливать вас тоже не будет.
Одним из методов решения этой ситуации является пометка описателей файлов как "готовых" в ассоциированной структуре данных после получения первого события, а затем игнорирование других событий, пока он в состоянии "готов". Когда вы читаете до получения EAGAIN, уберите флаг "готов" перед вызовом epoll_wait(2) снова на этом fd.
Если существует большое пространство ввода/вывода, то возможно, что при попытке его очистки другие файлы не будут обрабатываться, привода к недостатку данных. Этого обычно не происходит с epoll.
Решением будет поддержка списка готовности и помечение "готовых" описателей файлов в ассоциированной структуре данных, тем самым позволяя приложению запоминать файлы, требуемые для обработки, но все еще не обработанных среди уже "готовых" файлов. Это также поддерживает игнорирование последующих событий, получаемых вами для fd, которые уже "готовы".
Если вы используете кэш событий или храните все fd, возвращенные от epoll_wait(2), то убедитесь, что вы обеспечите способ его динамического закрытия (например - после обработки предыдущего события). Предположим, что вы получите 100 событий от epoll_wait(2), и что в событии #47 некоторое условие определяет, что событие #13 должно быть закрыто. Если вы уберете структуру и закроете close() fd для события #13, то ваш кэш событий все еще может сообщать о том, что есть ожидаемые события, непонятные для этого fd.
Одним из решений будет вызов во время обработки события 47 epoll_ctl(EPOLL_CTL_DEL) для удаления fd 13 и close(), а затем отметки его ассоциированной структуры данных как удаленной и связки его со списком очистки. Если найдется другое событие для fd 13 по работе, то ему сообщится, что fd уже было удалено и конфликтов не будет.
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |