epoll_wait () получает закрытый сокет дважды (read () / recv () возвращает 0) - PullRequest
3 голосов
/ 18 января 2011

У нас есть приложение, которое использует epoll для прослушивания и обработки http-соединений. Иногда epoll_wait () получает событие close на fd дважды в «строке». Значение: epoll_wait () возвращает соединение fd, по которому read () / recv () возвращает 0. Это проблема, поскольку у меня есть указатель malloc: ed, сохраненный в структуре epoll_event (struct epoll_event.data.ptr) и освобождаемый при fd (гнездо) определяется как закрытый в первый раз. Второй раз он падает.

Эта проблема возникает очень редко при реальном использовании (за исключением одного сайта, который на самом деле имеет около 500-1000 пользователей на сервер). Я могу воспроизвести проблему, используя http siege с> 1000 одновременных подключений в секунду. В этом случае ошибки приложения (из-за неверного указателя) происходят очень случайно, иногда через несколько секунд, обычно через десятки минут. Мне удалось воспроизвести проблему с меньшим количеством соединений в секунду, но для этого мне пришлось запускать приложение долго, много дней, даже недель.

Все новые соединения accept () fd: s устанавливаются как неблокирующие и добавляются в epoll как однократный запуск, запуск по фронту и ожидание доступности read (). Так почему же, когда нагрузка на сервер высока, epoll считает, что мое приложение не получило событие close и ставит новое в очередь?

epoll_wait () работает в своем собственном потоке и ставит в очередь события fd для обработки в другом месте. Я заметил, что было несколько закрытий, поступающих с простым кодом, который проверяет, происходит ли событие два раза подряд от epoll к одному и тому же fd Это случилось, и события, когда оба закрываются (recv (.., MSG_PEEK), сказали мне это:)).

Создан epoll fd:

epoll_create(1024);

epoll_wait () запускается следующим образом:

epoll_wait(epoll_fd, events, 256, 300);

новый fd устанавливается как неблокирующий после accept ():

int flags = fcntl(fd, F_GETFL, 0);
err = fcntl(fd, F_SETFL, flags | O_NONBLOCK);

новый fd добавлен в epoll (клиент имеет malloc: указатель структуры ed):

static struct epoll_event ev;
ev.events = EPOLLIN | EPOLLONESHOT | EPOLLET;
ev.data.ptr = client;
err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client->fd, &ev);

И после получения и обработки данных с fd, он перезагружается (конечно, начиная с EPOLLONESHOT). Поначалу я не использовал io с триггерным фронтом и неблокирующим интерфейсом, но я протестировал его и получил хороший прирост производительности, используя их. Эта проблема существовала до их добавления. Btw. shutdown (fd, SHUT_RDWR) используется в других потоках для запуска надлежащего события закрытия, которое будет получено через epoll, когда серверу необходимо закрыть fd из-за какой-либо http-ошибки и т. д. (я не знаю, является ли это правильным способом сделай это, но сработало отлично).

Ответы [ 5 ]

3 голосов
/ 20 июля 2011

Как только первое чтение () возвращает 0, это означает, что соединение было закрыто узлом.Почему ядро ​​генерирует событие EPOLLIN для этого случая?Ну, нет другого способа указать закрытие сокета, когда вы подписаны только на EPOLLIN.Вы можете добавить EPOLLRDHUP, который в основном совпадает с проверкой read (), возвращающей 0. Однако обязательно проверяйте этот флаг перед тем, как проверять EPOLLIN.Я приказал, чтобы эти блоки были релевантными, и return для EPOLLRDHUP также важен, потому что вполне вероятно, что deleteConnectionData () могла разрушить внутренние структуры.Поскольку EPOLLIN также устанавливается в случае закрытия, это может привести к некоторым проблемам.Игнорирование EPOLLIN безопасно, поскольку в любом случае оно не даст никаких данных.То же самое для EPOLLOUT, поскольку оно никогда не отправляется вместе с EPOLLRDHUP!

1 голос
/ 18 января 2011

epoll_wait () работает в своем собственном потоке и ставит в очередь события fd для обработки в другом месте. ... Так почему же, когда нагрузка на сервер высока, epoll считает, что мое приложение не получило событие close и ставит новое в очередь?

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

Может быть объект, на который указывает epoll_event.data.ptr, преждевременно освобождается до того, как событие epoll будет незарегистрировано в другом потоке, когда ваш сервер будет активно закрывать клиентское соединение.

Моей первой попыткой было бы запустить его под valgrind и посмотреть, нет ли в нем ошибок.

0 голосов
/ 30 декабря 2013

Регистрация сигнала 0x2000 для удаленного хоста закрытого соединения ex ev.events = EPOLLIN | ЭПОЛЛОНЕШОТ | ЭПОЛЛЕТ | 0x2000 и проверьте, если (flag & 0x2000) для удаленного хоста закрыть соединение

0 голосов
/ 01 февраля 2011

Удаление EPOLLONESHOT заставило проблему исчезнуть после нескольких других изменений. К сожалению, я не совсем уверен, что вызвало это. Использование EPOLLONESHOT с потоками и ручное добавление fd в очередь epoll, безусловно, было проблемой. Также указатель данных в структуре epoll освобождается после задержки. Отлично работает сейчас.

0 голосов
/ 18 января 2011

Я бы перепроверил себя в следующих разделах из epoll(7):

Q6
Будет ли закрытие файлового дескриптора причиной его удаленияиз всех наборов epoll автоматически?

и

o При использовании кэша событий ...

Там есть несколько хороших моментов.

...