epoll
От: maks1180  
Дата: 06.12.22 14:34
Оценка:
Задача стандартная — сетевое приложение, которое отвечает на входящие запросы.
Нужно получать уведомления:
1) EPOLLIN (на чтение) — если есть что прочитать из сокета (т.е. следим за состоянием).
2) EPOLLOUT (на запись) — только при смене состояния (т.е. следим за изменением состояния) на возможность записи, т.е. если раньше нельзя было писать а теперь можно.

В Windows через оконные сообщения, как раз так и работает, как я описал. Но в Линуксе, кажется нет хорошего решения.

Вариант №1 вызываем epoll_ctl( EPOLLET + EPOLLIN+EPOLLOUT)
не буду получать нотификацию EPOLLIN до следующих принятых данных, если прочитаю не всё что есть в сокете.
Как понять, что я всё прочитал из сокета ?

Вариант №2 вызываем epoll_ctl( EPOLLIN+EPOLLOUT) — без EPOLLET
буду постоянно получать EPOLLOUT, придёться постоянно вызывать epoll_ctl, что-бы снять или поставить EPOLLOUT. Это же функция ядра и вызов будет занимать время значительное ?
Вроде libuv так делает.

Вариант №3 вызывать 2 раза epoll_ctl для одного и тогоже сокета. Так можно ?
epoll_ctl(EPOLLIN)
epoll_ctl(EPOLLOUT+EPOLLET)


Какие ещё есть решения ?
Может быть сделать 2 epoll, один для чтения, другой для записи ? Тогда их из разных потоков нужно вызывать epoll_wait ?
===============================================
(реклама, удалена модератором)
Отредактировано 06.12.2022 14:52 maks1180 . Предыдущая версия . Еще …
Отредактировано 06.12.2022 14:47 maks1180 . Предыдущая версия .
Отредактировано 06.12.2022 14:41 maks1180 . Предыдущая версия .
Отредактировано 06.12.2022 14:39 maks1180 . Предыдущая версия .
Отредактировано 06.12.2022 14:37 maks1180 . Предыдущая версия .
Отредактировано 06.12.2022 14:35 maks1180 . Предыдущая версия .
Re: epoll
От: vopl Россия  
Дата: 06.12.22 15:18
Оценка:
Здравствуйте, maks1180, Вы писали:

M>Задача стандартная — сетевое приложение, которое отвечает на входящие запросы.

M>Нужно получать уведомления:
M>1) EPOLLIN (на чтение) — если есть что прочитать из сокета (т.е. следим за состоянием).
M>2) EPOLLOUT (на запись) — только при смене состояния (т.е. следим за изменением состояния) на возможность записи, т.е. если раньше нельзя было писать а теперь можно.

M>В Windows через оконные сообщения, как раз так и работает, как я описал. Но в Линуксе, кажется нет хорошего решения.


M>Вариант №1 вызываем epoll_ctl( EPOLLET + EPOLLIN+EPOLLOUT)

M>не буду получать нотификацию EPOLLIN до следующих принятых данных, если прочитаю не всё что есть в сокете.
M>Как понять, что я всё прочитал из сокета ?

чтение вернет данных меньше чем размер предоставленного под чтение буфера, либо чтение вернет ноль данных и диагностику EAGAIN (сокет неблокирующий)

M>Вариант №2 вызываем epoll_ctl( EPOLLIN+EPOLLOUT) — без EPOLLET

M>буду постоянно получать EPOLLOUT, придёться постоянно вызывать epoll_ctl, что-бы снять или поставить EPOLLOUT. Это же функция ядра и вызов будет занимать время значительное ?
M>Вроде libuv так делает.

M>Вариант №3 вызывать 2 раза epoll_ctl для одного и тогоже сокета. Так можно ?

M>epoll_ctl(EPOLLIN)
M>epoll_ctl(EPOLLOUT+EPOLLET)


M>Какие ещё есть решения ?


Сделать адаптер из ET в LT.
За базовый — берем ET. Для записи — используем его как есть.
Для чтения — заводим дополнительный булевый флажок со смыслом "есть что читать", по умолчанию сброшен, при получении EPOLLIN — взводим, при неблокирующем чтении — если вычитано меньше данных нежели предоставленный под чтение буфер или вычитано 0 и выдана диагностика EAGAIN — сбрасываем. Чтение, естественно, неблокирующее, чтобы вместо блока был EAGAIN. Вот и все, булевый флажок в основном эквивалентен предикату "есть что читать", остается лишь малый кейс при котором это не так — если при очередном чтении было вычитано данных ровно под размер буфера и именно такое количество данных и было доступно.. Но, это легко отрулить.
Re[2]: epoll
От: maks1180  
Дата: 06.12.22 15:37
Оценка:
M>>Вариант №1 вызываем epoll_ctl( EPOLLET + EPOLLIN+EPOLLOUT)
M>>не буду получать нотификацию EPOLLIN до следующих принятых данных, если прочитаю не всё что есть в сокете.
M>>Как понять, что я всё прочитал из сокета ?

V>чтение вернет данных меньше чем размер предоставленного под чтение буфера, либо чтение вернет ноль данных и диагностику EAGAIN (сокет неблокирующий)

В этом можно быть уверенным ? Не может быть так что вернёт меньше и ещё останется ?


M>>Какие ещё есть решения ?


V>Сделать адаптер из ET в LT.

V>За базовый — берем ET. Для записи — используем его как есть.
V>Для чтения — заводим дополнительный булевый флажок со смыслом "есть что читать", по умолчанию сброшен, при получении EPOLLIN — взводим, при неблокирующем чтении — если вычитано меньше данных нежели предоставленный под чтение буфер или вычитано 0 и выдана диагностика EAGAIN — сбрасываем. Чтение, естественно, неблокирующее, чтобы вместо блока был EAGAIN. Вот и все, булевый флажок в основном эквивалентен предикату "есть что читать", остается лишь малый кейс при котором это не так — если при очередном чтении было вычитано данных ровно под размер буфера и именно такое количество данных и было доступно.. Но, это легко отрулить.

Вопрос, что делать потом с эти флажком ? Заставить дочитать сокет до конца сейчас или после следующего вызова epoll_wait() ?

Я сейчас читаю всё до конца. Но мне не нравится такое решение — если будет клиент который шлёт много данных, я буду тратить на него много ресурсов и латентность приложения увеличится.
Я бы лучше читал по 8-32кб за 1 раз, потом epoll_wait() и снова читать то что недочитано. Мне кажетья таким способом латентность будет лучше, чем когда всё читать до конца.
===============================================
(реклама, удалена модератором)
Re[3]: epoll
От: vopl Россия  
Дата: 06.12.22 16:14
Оценка:
Здравствуйте, maks1180, Вы писали:

M>>>Вариант №1 вызываем epoll_ctl( EPOLLET + EPOLLIN+EPOLLOUT)

M>>>не буду получать нотификацию EPOLLIN до следующих принятых данных, если прочитаю не всё что есть в сокете.
M>>>Как понять, что я всё прочитал из сокета ?

V>>чтение вернет данных меньше чем размер предоставленного под чтение буфера, либо чтение вернет ноль данных и диагностику EAGAIN (сокет неблокирующий)

M>В этом можно быть уверенным ? Не может быть так что вернёт меньше и ещё останется ?
Прям четких гарантий я не встречал, но на практике оно ведет себя именно так.

M>>>Какие ещё есть решения ?


V>>Сделать адаптер из ET в LT.

V>>За базовый — берем ET. Для записи — используем его как есть.
V>>Для чтения — заводим дополнительный булевый флажок со смыслом "есть что читать", по умолчанию сброшен, при получении EPOLLIN — взводим, при неблокирующем чтении — если вычитано меньше данных нежели предоставленный под чтение буфер или вычитано 0 и выдана диагностика EAGAIN — сбрасываем. Чтение, естественно, неблокирующее, чтобы вместо блока был EAGAIN. Вот и все, булевый флажок в основном эквивалентен предикату "есть что читать", остается лишь малый кейс при котором это не так — если при очередном чтении было вычитано данных ровно под размер буфера и именно такое количество данных и было доступно.. Но, это легко отрулить.

M>Вопрос, что делать потом с эти флажком ? Заставить дочитать сокет до конца сейчас или после следующего вызова epoll_wait() ?


В смысле что делать с флажком? Это тебе признак того что "есть что читать" для того чтобы вести чтение по режиму LT при записи ET. Вот этот флажок и есть маркер LT для чтения. Если не взведен — данных нет, можно не читать, все равно не получится. Если взведен — читай если хочешь и получишь данные. Можешь дочитать до конца тогда флажок сбросится. Можешь не дочитывать до конца, тогда флажок останется взведенным и вэлкам почитать ещё если есть такое желание.

M>Я сейчас читаю всё до конца. Но мне не нравится такое решение — если будет клиент который шлёт много данных, я буду тратить на него много ресурсов и латентность приложения увеличится.

M>Я бы лучше читал по 8-32кб за 1 раз, потом epoll_wait() и снова читать то что недочитано. Мне кажетья таким способом латентность будет лучше, чем когда всё читать до конца.
Оставлять чтение на потом (вместо push модели переходить на pull) конечно можно, и этим, например, решаются вопросы пересыщения локальных буферов если входящий трафик превосходит возможности его обработки... Но про латентность не вполне понятно, что имеешь ввиду. Посмотри на TCP, какие у него буфера с одной и с другой стороны, что такое окно, на каких режимах все это хозяйство эффективно использует канал связи, какие эффекты канала убивают эффективность, какие алгоритмы управления окном бывают в TCP, как они себя ведут на том или ином типе трафика и канале связи..
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.