Мультиплексный Ввод-Вывод



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

Преследуются следующие цели:

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

  2. Полностью демонтируйте основу, на которой строятся приложения для обслуживания нескольких клиентов.

  3. Разработайте стратегию для будущего приложения Python, которое должно обслуживать множество клиентов.

  4. Создайте в голове четкую картинку (не зря говорят, что понимаешь, когда можешь это объяснить)


За что?

Годы программирования на PHP принесли свои плоды — я никогда не задавался вопросом, что происходит за кулисами.

Но когда проекты начали медленно, но верно тормозить, я решил, что пора опомниться и начал изучать Python (чем с успехом занимаюсь до сих пор).

Но так как мне совсем надоели всякие слова типа «форк», «сокет», «eventloop», «мультипроцессинг», «epoll» и т.п.

, я решил копнуть глубже.

Что из этого получилось, решать вам.

Однако существует некоторая путаница.

Подтверждение этому можно найти по ссылкам [ 6 , 7 ]

интервал основной()

В этой статье обсуждается ввод-вывод на базе ОС Linux версии 2.6 и старше.

Поскольку материал во многом ориентирован на таких, как я (устройство для нагрева воды, опционально оснащенное звуковой сигнализацией), вам придется пройти много основ, поэтому просто пропустите ненужные вам моменты.



Базовые концепты
Файл — это фундаментальная абстракция в Linux. Linux придерживается философии «все есть файл», что означает, что большая часть взаимодействия осуществляется посредством чтения и записи файлов.

Операции с файлами осуществляются с использованием уникального дескриптора — файлового дескриптора или fd. Большая часть системного программирования в Linux состоит из работы с файловыми дескрипторами.

Существовать обычные файлы (обычный файл) – это то, к чему мы привыкли (самый обычный «файл» в обычном понимании) и специальные файлы — это некоторые объекты, которые представлены в виде файлов.

Linux поддерживает 4 типа специальных файлов:

  • Блокировать файлы устройства
  • Файлы символьных устройств ввода-вывода
  • Именованные конвейеры (именованный канал или FIFO)
  • Розетки
Последнее как раз и является нашей областью интересов, поскольку сокеты обеспечивают связь между двумя разными процессами, которые могут находиться на разных компьютерах (клиент-сервер).

На самом деле сетевое программирование и программирование для Интернета построено именно на сокетах.

В первом приближении достаточно рассматривать только обычные файлы и сокеты.



Модели ввода-вывода
Всего в Unix-подобных системах доступно 5 + 1 различных моделей ввода/вывода.

О «Plus One» я расскажу чуть позже, а пока давайте рассмотрим каждую модель подробнее.

Блокировка ввода-вывода По умолчанию весь ввод-вывод осуществляется в блокирующем стиле.

Рассмотрим схематичное изображение процессов, происходящих при блокировке ввода/вывода.



Мультиплексный ввод-вывод

В этом случае процесс выполняет системный вызов Получение из .

В результате процесс блокируется (переходит в спящий режим) до тех пор, пока не поступят данные и системный вызов не запишет их в буфер приложения.

После этого системный вызов завершается (возврат ОК) и мы можем обрабатывать наши данные.

Очевидно, что у такого подхода есть очень большой недостаток — пока мы ждём данные (а это может занять очень много времени из-за качества соединения и т. д.), процесс спит и не отвечает на запросы.

Неблокирующий ввод-вывод Мы можем установить неблокирующий режим при работе с сокетами, по сути говоря ядру следующее: «Если ввод-вывод, который мы хотим выполнить, невозможен без помещения процесса в блок (спящий режим), то верните мне ошибку.

что вы не можете сделать это без блокировки».

Рассмотрим схематическое изображение процессов, происходящих при неблокирующем вводе/выводе.



Мультиплексный ввод-вывод

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

В результате ядро запишет данные в буфер процесса и они станут доступны для обработки.

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

Этот режим называется опросом, поскольку приложение постоянно опрашивает ядро системы на наличие данных.

В принципе, я не вижу никаких ограничений на последовательный опрос нескольких сокетов и, соответственно, чтение из первого, содержащего данные.

Такой подход приводит к большим накладным затратам процессорного времени.

Мультиплексирование ввода-вывода Вообще слово мультиплексирование переводится как «уплотнение».

Мне кажется, это удачно можно охарактеризовать девизом тайм-менеджмента – «научись делать больше».

При мультиплексировании ввода-вывода мы обращаемся к одному из системных вызовов, доступных в ОС (мультиплексору, такому как select, poll, pselect, dev/poll, epoll (рекомендуется для Linux), kqueue (BSD)) и блокируем его вместо блокировка фактического вызова ввода-вывода.

Схематически процесс мультиплексирования показан на изображении

Мультиплексный ввод-вывод

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

Затем ядро возвращает нам статус удобочитаемый и вы можете получить данные, используя Получение из .

На первый взгляд, это полное разочарование.

Та же блокировка, ожидание и еще 2 системных вызова (select и Recvfrom) – большие накладные расходы.

Но в отличие от метода блокировки, select (да и любой другой мультиплексор) позволяет ждать данных не от одного, а от нескольких файловых дескрипторов.

Надо сказать, что это наиболее разумный метод обслуживания многих клиентов, особенно если ресурсы весьма ограничены.

Почему это так? Потому что мультиплексор сокращает время простоя (сна).

Я попытаюсь объяснить с помощью следующего изображения

Мультиплексный ввод-вывод

Создается пул дескрипторов, соответствующих сокетам.

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

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

Теперь внимание! Самое важное! Ответьте на вопрос: какое событие произойдет с большей вероятностью? Означает ли событие A, что данные будут готовы в определенном сокете, или событие B означает, что данные будут готовы хотя бы в одном сокете? Ответ: Б В случае мультиплексирования мы проверяем ВСЕ сокеты в цикле и берем первый, который готов.

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

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

мы ошибемся на 100% и будем ждать, но хотя мы не могли терять это время Ввод-вывод в потоках/дочерних процессах (одно описание файла на поток или процесс) Сказав вначале, что существует метод 5+1, я имел в виду именно такой подход, когда используется несколько потоков или процессов, каждый из которых производит блокирующий ввод-вывод. Это похоже на мультиплексирование ввода-вывода, но имеет ряд недостатков.

Всем известно, что потоки в Linux довольно дороги (с точки зрения системных команд), поэтому использование потоков влечет за собой увеличение накладных расходов.

Кроме того, если рассматривать Python как язык программирования, он имеет GIL и, соответственно, в рамках 1 процесса в любой момент времени может выполняться только один поток.

Другой вариант — создать дочерние процессы для обработки ввода-вывода в блокирующем стиле.

Но тогда нужно продумать взаимодействие между процессами (IPC — межпроцессное общение), что имеет некоторые трудности.

Более того, если общее количество ядер не превышает одного, то польза от такого подхода сомнительная.

Кстати, насколько мне известно, Apache работает примерно по такой схеме (префорк MPM или потоки), обслуживая клиента либо в потоке, либо в отдельном процессе.

Сигнальный ввод-вывод Можно использовать сигналы, заставляя ядро отправлять нам сигнал типа СИГИО , когда становится возможным чтение данных без блокировки (дескриптор готов к чтению).

Схематически этот подход показан на изображении.



Мультиплексный ввод-вывод

Сначала вам нужно задать параметры сокета для работы с сигналами и назначить обработчик сигнала с помощью системного вызова sigaction. Результат возвращается мгновенно, поэтому приложение не блокируется.

По сути, всю работу делает ядро, потому что оно отслеживает готовность данных и отправляет нам сигнал SIGIO, который вызывает установленный на нем обработчик (функция обратного вызова).

Соответственно, сам вызов Recvfrom может быть сделан либо в обработчике сигнала, либо в основном потоке программы.

Насколько я могу судить, здесь есть одна проблема — для каждого процесса такого типа может быть только один сигнал.

Те.

мы можем работать только с одним фд одновременно (хотя я не уверен) Асинхронный ввод-вывод Асинхронный ввод-вывод осуществляется с помощью специальных системных вызовов.

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

В этом основное отличие данной реализации от реализации, основанной на сигналах.

Процессы асинхронного ввода/вывода схематически показаны на изображении.



Мультиплексный ввод-вывод

Делаем системный вызов aio_read и указываем все необходимые параметры.

Остальную работу за нас делает ядро.

Конечно, должен быть механизм уведомления процесса о завершении ввода-вывода.

И здесь потенциально возникает масса проблем.

Но об этом в другой раз.

Вообще с этим термином связано много проблем; примеры ссылок уже приводились.

Часто происходит путаница понятий между асинхронным, неблокирующим и мультиплексным вводом/выводом, видимо, потому, что само понятие «асинхронный» можно трактовать по-разному.

В моем понимании асинхронный означает независимый во времени.

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

На практике
На практике разные модели комбинируются исходя из поставленной задачи.

Действуйте следующим образом:

  • Используется потоком/процессом для каждой операции с блокировкой – невыгодно
  • Множество клиентов в разных потоках/процессах.

    Каждый поток/процесс использует мультиплексор

  • Множество клиентов в разных потоках/процессах.

    Используется асинхронный ввод-вывод (aio).

  • Просто встройте сервер в ядро
Более подробная информация в Проблема C10K

Нижняя граница

Надеюсь, теперь станет хоть немного понятнее различия в тех вещах, в которых легко запутаться.

Ну да, правила мультиплексирования (думаю, пока не доработают aio).

Изучая справочную литературу, я пришел к выводу, что в отличие от сокетов обычные файлы нельзя перевести в неблокирующий режим.

Кажется, для них доступен Aio, который обсуждается здесь: Асинхронный ввод-вывод в Linux или добро пожаловать в ад

Литература и ссылки

  1. Роберт Лав "Linux. Системное программирование"
  2. В.

    Ричард Стивенс, Билл Феннер, Эндрю М.

    Рудофф «Сетевое программирование для Unix, том 1, третье издание: API сетевых сокетов»

  3. Стивенс Р.

    , Раго С.

    «Unix. Профессиональное программирование, 2-е издание».

  4. Всеобщий любимец Проблема C10K
  5. Асинхронный ввод-вывод в Linux или добро пожаловать в ад
  6. Сравнение двух шаблонов проектирования высокопроизводительного ввода-вывода
  7. Асинхронный и неблокирующий
  8. Блокирующие и неблокирующие сокеты
Теги: #асинхронное программирование #мультиплексирование #неблокирующие сокеты #мультиплексор #aio #python #разработка веб-сайтов
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.