LINUX.ORG.RU

как нормально использовать мультиплексирование?

 , , ,


0

1

в общем очередной раз пытался разобраться как нормально использовать мультиплексирование I/O... примеров из Стивенса(разработка сетевых приложений unix) явно недостаточно. везде речь идет о реакторе/проакторе, разницу так и не понял(да и инфы, если честно, по пальцам пересчитать)... везде фигурирует очередь событий некая. нашел еще вот это https://www.opennet.ru/base/dev/epoll_example.txt.html в итоге совсем не понятно стало... объясните как эффективно юзать i/o multiplexing?

допустим для poll: я так думаю в одном треде висим на accept, в другом - бесконечный цикл где используется poll). когда клиент подключается, то добавляем этот новый сокет в опрашиваемые, однако как это сделать не понятно(у нас же бесконечный цикл на poll). да и вообще может все не так надо использовать. в общем, напишите, кому не сложно псевдокод или хотя бы схематично, а то у меня кроме как примера из вышеуказанной книжки(Стивенс), где в одном потоке и accept и poll, ничто на ум не приходит

poll удобен, когда ты на соединение создаешь, например, свой обработчик, и вот внутри этого обработчика используешь poll. Много соединений - много обработчиков. Каждый обработчик вызывает свой poll внутри своего уже цикла.

Здесь poll выступает просто как замена select, но не подверженная известному ограничению по количеству дескрипторов, которое есть для select.

А если ты хочешь как-то по другому управлять мультиплексированием по сокетам, то тут тогда лучше смотреть в сторону epoll. Там должно быть что-то вроде подписки на приход данных, но честно скажу, что с epoll напрямую не работал.

Ранее я использовал select/poll и asio. Вот в asio модель ближе к epoll. Только детали я уже стал забывать :)

dave ★★★★★
()

Грубо говоря, если создавать отдельный поток на каждое соединение, то можно использовать poll.

Если же у тебя будет только ограниченный пул потоков по обслуживанию всех соединений, то лучше смотреть в сторону epoll (asio).

Один поток на все соединения - это вырожденный случай пула потоков. Такую модель использует libev, если мне не изменяет память

dave ★★★★★
()
Последнее исправление: dave (всего исправлений: 1)

Возможен еще такой вариант. У тебя обработчик держит несколько фиксированных соединений (два-три) по сокетам, а также имеет свой собственный поток исполнения. Тогда тоже можно использовать poll.

dave ★★★★★
()

открываете исходники boost/ACE/libev/libevent и изучаете

anonymous
()
Ответ на: комментарий от dave

Грубо говоря, если создавать отдельный поток на каждое соединение, то можно использовать poll.

хм, что поллить внутри соединения то?

у меня просто из башки никак не выйдет пример из Стивенса, где в одном цикле был poll и на подключаемых клиентов и на приход данных с них

меня вот интересует как примерно такое же(хоть и сильно упрощенно) реализовать на си: http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/async.html

xperious ★★
() автор топика
Ответ на: комментарий от dave

Возможен еще такой вариант. У тебя обработчик держит несколько фиксированных соединений (два-три) по сокетам, а также имеет свой собственный поток исполнения. Тогда тоже можно использовать poll.

а можете схематично изобразить, а то не пойму как согласовать 2-3 потока и поток исполнения(для чего он вообще?)

xperious ★★
() автор топика
Ответ на: комментарий от xperious

Эти картинки больше касаются epoll, который на линуксе будет использовать asio. Библиотека libev - тоже будет использовать epoll.

Poll - это рефакторинг для select. Можно и poll в одном цикле для многих сокетов, но зачем, если есть epoll?

dave ★★★★★
()
Ответ на: комментарий от xperious

Да пока предлагаю не забивать этим голову. Для простоты можно считать, что один поток обслуживает один сокет. Это для poll

Вся фишка этих функций в том, что они как и sleep, и ожидание на блокировке не вешают систему, т.е. не грузят процессор

dave ★★★★★
()
Ответ на: комментарий от xperious

В общем, я бы взял библиотеку libev и что-нибудь простенькое сварганил, где один поток обслуживает, скажем, десятки клиентов

dave ★★★★★
()
Ответ на: комментарий от dave

Эти картинки больше касаются epoll, который на линуксе будет использовать asio. Библиотека libev - тоже будет использовать epoll.

по поводу boost::asio мне не совсем ясны два момента: 1) по поводу Asynchronous Operation Processor: это в нем, получается бесконечный цикл то находится, или все-таки внутри proactor(вроде им является boost::asio::io_service)?

2) по поводу Asynchronous Event Demultiplexer «This is implemented by waiting on an event or condition variable until a completion handler is available in the completion event queue.» вот тут я вообще не пойму как через condition variable можно связать event queue и Asynchronous Event Demultiplexer... это один какой-то поток должен через condition variable другой поток оповещать о чем то, а что в этих потоках находится не понятно... в общем жуть

Да пока предлагаю не забивать этим голову. Для простоты можно считать, что один поток обслуживает один сокет. Это для poll

я никак не могу понять связь между «один поток - один сокет» и «poll»... это ж совершенно ортогональные способы обработки множества клиентов: нативный поток на соединение(1) и мультиплексирование(2)

В общем, я бы взял библиотеку libev и что-нибудь простенькое сварганил, где один поток обслуживает, скажем, десятки клиентов

хм, думаю пока подразберусь с libuv https://habrahabr.ru/post/336498/

xperious ★★
() автор топика
Ответ на: комментарий от dave

Грубо говоря, если создавать отдельный поток на каждое соединение, то можно использовать poll.

Не надо вводить читателей в заблуждение. Число потоков и выбор между poll и epoll никак не связаны. Основное преимущество epoll перед poll это отсутствие необходимости каждый раз в ядро пихать массив struct pollfd.

С poll() всё можно делать в одном-единственном потоке, и так даже проще. Любое запрошенное событие вызывает возврат из poll(). Пришло новое соединение? Пришли данные по какому-то из соединений? Можно писать в сокет? Произошла ошибка? Все эти события прерывают выполнение poll(). Конечно, это если ты просил об этих событиях во флагах.

i-rinat ★★★★★
()

https://www.opennet.ru/base/dev/epoll_example.txt.html

Там неудачное написано вот тут:

ev.data.fd = client_sock;

Поле data это union. Если туда положить номер файлового дескриптора, то больше ничего не влезет. Лучше туда класть указатель на контекст соединения, размещённый в куче. А в нём уже можно сколько угодно данных хранить, в том числе и fd.

i-rinat ★★★★★
()
Ответ на: комментарий от dave

Прочитав твоё сообщение, можно подумать, что poll() можно использовать только в модели «поток на каждое соединение». Это же не так. Более того, если на каждое соединение отдельный поток, зачем вообще poll()? Работай себе в блокирующем режиме, так даже проще.

i-rinat ★★★★★
()
Ответ на: комментарий от dave

Тогда помоги, пожалуйста, новичку разобраться :)

Лучший способ разобраться — решать задачи. Если реальной задачи нет, могу предложить:

  1. Сервис подсчитывания MD5. Принимает соединение, читает данные, пока не встретит два \n подряд (\n\n). Считает MD5, отправляет его текстовое представление в ответ, закрывает соединение.
  2. Сервис времени. Принимает соединение, затем каждую секунду (примерно) отправляет строчку с текущим временем клиенту, пока клиент не закроет соединение.
  3. TCP-прокси. Принимает соединение, соединяется с бекендом, пересылает данные в обе стороны, пока какая-то из сторон не закроет соединение.

Расположены в порядке увеличения боли от кодирования.

В процессе решения будут возникать вопросы, связанные не с тем, как функции вызываются, а с вопросами организации программ. Их решение это тоже часть задачи.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

хм, ну вот, допустим, по первому как-то так: если это считать имитацией каких-то сложных вычислений, то логично сделать префорк либо пул потоков, ну и отдельно в них и считать md5 и возвращать...

по поводу других надо еще подумать...вообще спасибо за пищу для размышлений

xperious ★★
() автор топика
Ответ на: комментарий от i-rinat

Лучший способ разобраться — решать задачи.

согласен, и реальные задачи решались, но только готовыми инструментами: boost.asio, qt, go... но надоело что сути их не понимаю, решил какое-то подобие boost.asio с нуля сделать на си... ну и на проакторе сразу в лужу сел

xperious ★★
() автор топика
Ответ на: комментарий от anonymous

а можете толком объяснить разницу?) нихера не понятно, сколько ни пытался разобраться... типа проактор это типа асинхронного реактора... в общем не понятно

xperious ★★
() автор топика
Ответ на: комментарий от xperious

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

Пул потоков это попытка усложнить себе жизнь, добавив в задачу чего-то такое, что в ней изначально не было.

i-rinat ★★★★★
()
Ответ на: комментарий от xperious

если совсем кратко на пальцах,реактор обслуживает наступившие события, проактор обслуживает завершенные события
для понимания этого нужно знать IO windows,где апи записи чтения имеют IOCompletition процедуры,
так вот виндовый IO как раз больше вписывается в модель проактора, в линуксе такого нет(можно эмулировать-имитировать),
фокус виндового IO что процедуры Competition запускаются виндой в отдельных потоках, т.е. к основному IO циклу он никак не завязан, в линуксе для имитации такого асинхронного ио для проактора, заводят отдельный трейд, который будет обрабатывать Completition процедуры(фукции аля события)
для лучшего понимания лучше почитать литерадурку
https://stackoverflow.com/questions/11859332/reactor-vs-proactor
http://www.artima.com/articles/io_design_patternsP.html
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx

anonymous
()
Ответ на: комментарий от xperious

в самом простом случае, когда количество запросов на коннект не велико, можно все в одном цикле крутить, когда запросов на обслуживание дохера,то аксепт выносят в отдельный поток,а иногда и в несколько,выше на хабре ссылка там описано все варианты потоков+епулов

anonymous
()
Ответ на: комментарий от anonymous

ну в общем той статьи с хабра мне как раз и не хватало

xperious ★★
() автор топика
24 октября 2017 г.
Ответ на: комментарий от i-rinat

Сервис времени. Принимает соединение, затем каждую секунду (примерно) отправляет строчку с текущим временем клиенту, пока клиент не закроет соединение.

такой сервис лучше делать на основе SIGIO?

xperious ★★
() автор топика
Ответ на: комментарий от i-rinat

да у стивенса просто в главе про SIGIO вычитал про ntp-сервер на основе SIGIO...

а вопросы, по идее, другие хотел задать: вот обдумываю я сейчас реализацию eventloop-а и для сетевых сокетов и для дискового I/O... я так понял, что epoll для, например, текстовых файлов не предназначен совсем. можно использовать либо posix aio либо aio уровня ядра...

1) posix aio реально используется в 2017 году? я думал что создавать поток чисто для дискового i/o это неэффективно

2) aio уровня ядра, все ли нормально с ним в linux? толком так и не понял: то ли не доделали его, то ли косячный какой-то он

xperious ★★
() автор топика
Последнее исправление: xperious (всего исправлений: 2)
Ответ на: комментарий от xperious

Этого всего не знаю. Я как-то пробовал звук выводить на сигналах, словил боль. Погуглил, нагуглил больше боли и статью о том, как плохо использовать сигналы. Особенно в библиотеках.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

хм, думаю тогда надо отдельную тему создать...

а по поводу неиспользования файлового I/O в epoll я прав?

xperious ★★
() автор топика
Ответ на: комментарий от xperious

а по поводу неиспользования файлового I/O в epoll я прав?

Насколько я знаю, для файлового ввода-вывода поллинг не работает.

i-rinat ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.