Модель мультиплексирования ввода-вывода синхронная, асинхронная, блокирующая, неблокирующая и подробный пример

Python

Введение в модель ввода-вывода

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

Давайте поговорим об объектах и ​​шагах, связанных с вводом-выводом:

*Для сетевого ввода-вывода (в качестве примера мы берем read) будут задействованы два системных объекта:*

  • Одним из них является процесс (или поток), который вызывает этот ввод-вывод.
  • Одним из них является ядро ​​системы (ядро)

* Когда происходит операция чтения, она проходит две фазы:*

  • Ожидание подготовки данных, например, accept(), recv() ожидание данных(Waiting for the data to be ready)
  • Скопировать данные из ядра в процесс.Например, accept() получает запрос.После того, как recv() получает данные, отправленные соединением, их нужно скопировать в ядро, а затем скопировать из ядра в процесс пользователя пространство. (Copying the data from the kernel to the process)

*Для потоков сокетов поток данных проходит два этапа:*

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

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

Блокирующий ввод-вывод (блокирующий ввод-вывод)

В linux все сокеты по умолчанию заблокированы.Типичный поток операции чтения выглядит так:

阻塞IO流程

Когда пользовательский процесс вызывает системный вызов recvfrom, ядро ​​запускает первый этап ввода-вывода: подготовка данных (для сетевого ввода-вывода много раз данные не поступали в начале. Например, полный UDP-пакет еще не получен. , В это время ядро ​​будет ждать поступления достаточного количества данных). Этот процесс должен ждать, то есть данные копируются вБуфер ядра ОСТребуется процесс. На стороне пользовательского процесса весь процесс будет заблокирован (конечно, сам процесс выбирает блокировку). Когда ядро ​​ждет, пока данные будут готовы, оноКопировать данные из ядра в пользовательскую память, после чего ядро ​​возвращает результат, а пользовательский процесс освобождает состояние блока и снова начинает работать.

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

Неблокирующий ввод-вывод (неблокирующий ввод-вывод)

В Linux вы можете сделать его неблокирующим, установив socket. При выполнении операции чтения на неблокирующем сокете поток выглядит следующим образом:

非阻塞 I/O 流程

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

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

Стоит отметить, что неблокирующий ввод-вывод в настоящее время применяется только к ожидающим данным. Когда для выполнения recvfrom поступают реальные данные, ввод-вывод по-прежнему блокируется синхронно. Это видно из копирования данных из ядра в пользователя. на рисунке

Мультиплексирование ввода-вывода (мультиплексирование ввода-вывода)

Мультиплексирование ввода-вывода — это то, что мы называем select, poll и epoll.В некоторых местах этот метод ввода-вывода также называется вводом-выводом, управляемым событиями. Преимущество select/epoll заключается в том, что один процесс может обрабатывать ввод-вывод нескольких сетевых подключений одновременно. Его основной принцип заключается в том, что функции select, poll и epoll будут непрерывно опрашивать все сокеты, за которые они отвечают, и когда в определенный сокет поступают данные, он уведомляет пользовательский процесс.

I/O 多路复用流程

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

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

В модели мультиплексирования ввода-вывода на практикеДля каждого сокета обычно устанавливается неблокирующий режим, потому что, только установив его в неблокирующий режим, отдельный поток/процесс не может быть заблокирован (или заблокирован) и может продолжать обрабатывать другие сокеты. Как показано на рисунке выше, весь процесс пользователя фактически постоянно блокируется. Просто процесс блокируется функцией select, а не сокетом IO.

Когда пользовательский процесс вызовет select, весь процесс будет заблокирован, и в то же время все сокеты входящих соединений будут добавлены в список мониторинга select, а ядро ​​будет «мониторить» все сокеты, отвечающие за select., а затем функция select (poll, epoll и т. д.) будет непрерывно опрашивать все сокеты, за которые она отвечает. Эти сокеты не блокируются и существуют в списке мониторинга выбора. прибытие данных. Когда данные в любом сокете будут готовы, select вернется. В это время пользовательский процесс снова вызывает операцию чтения, чтобы скопировать данные из ядра в пользовательский процесс.

Комментарии:
*Мультиплексирование ввода-вывода характеризуется механизмом, при котором процесс может одновременно ожидать несколько файловых дескрипторов,* И любой из этих дескрипторов файлов (дескрипторов сокетов) переходит в состояние готовности к чтению, и функция select() может вернуться.
* Таким образом, мультиплексирование ввода-вывода по сути не будет иметь параллельной функции, потому что в любой момент времени работает только один процесс или поток, это может повысить эффективность, поскольку selectepoll помещает входящие сокеты в свой «монитор». В списке, когда какой-либо сокет имеет данные доступны для чтения и записи, они будут обработаны немедленно. Если selectepoll обнаружит много сокетов одновременно, он немедленно вернется в процесс для обработки. Это лучше, чем один сокет, блокирующий и ожидающий, и обработка более эффективна.*
Конечно, он также может быть многопоточным/многопроцессным, одно соединение используется для открытия процесса/обработки потока, поэтому потребление памяти и страницы переключения процессов будут потреблять больше системных ресурсов.
Таким образом, мы можем комбинировать мультиплексирование ввода-вывода и многопроцессорность/многопоточность для достижения высокопроизводительного параллелизма.Мультиплексирование ввода-вывода отвечает за повышение эффективности уведомлений о получении сокетов.После получения запроса он передается в пул/поток процессов. пул для обработки логики.

Асинхронный ввод-вывод (асинхронный ввод-вывод)

Асинхронный ввод-вывод под Linux на самом деле редко используется. Давайте посмотрим на его процесс:

异步IO 流程

После того как пользовательский процесс инициирует операцию чтения, он может немедленно приступить к другим действиям. С другой стороны, с точки зрения ядра, когда оно получает асинхронное чтение, оно немедленно возвращается, поэтому оно не будет генерировать никакого блока для пользовательского процесса. Затем ядро ​​будет ждать готовности данных, а затем скопирует данные в память пользователя.Когда все это будет сделано, ядро ​​отправит сигнал пользовательскому процессу, сообщая ему, что операция чтения завершена.

Разница и связь между блокирующим вводом-выводом, неблокирующим вводом-выводом и синхронным вводом-выводом, асинхронным вводом-выводом

Блокирующий ввод-вывод против неблокирующего ввода-вывода:

Концепция: блокировка и неблокировка касаютсяСостояние программы в ожидании результата вызова (сообщение, возвращаемое значение).Блокирующий вызов означает, что текущий поток приостанавливается до тех пор, пока не будет возвращен результат вызова. Вызывающий поток не возвращается, пока не получит результат. Неблокирующий вызов означает, что вызов не будет блокировать текущий поток до тех пор, пока результат не будет немедленно доступен.

Пример: вы звоните владельцу книжного магазина, чтобы узнать, доступна ли книга «Распределенные системы». -блокировка звонка, вам все равно, говорит вам босс или нет, вы идете играть первым, конечно же, вам также нужно изредка проверять, вернул ли босс результат через несколько минут. Здесь блокировка и неблокировка не имеют ничего общего с синхронностью или асинхронностью. Это не имеет ничего общего с тем, как ваш босс отвечает на ваши результаты.

Анализ: блокирующий ввод-вывод заблокирует соответствующий процесс до завершения операции, в то время как неблокирующий ввод-вывод вернется немедленно, когда ядро ​​все еще готовит данные.

Синхронный ввод-вывод против асинхронного ввода-вывода:

Концепция: синхронный и асинхронный. Синхронный и асинхронный фокус на* Механизм обмена сообщениями(синхронная связь/ асинхронная связь) Так называемая синхронизация заключается в отправкеперечислить, прежде чем получить результат,перечислитьне возвращаются. Но как только вызов возвращается, вы получаете возвращаемое значение. Другими словами, поабонентактивно жду этогоперечислитьрезультат. Асинхронность наоборотперечислитьПосле выдачи вызов возвращается напрямую, поэтому результат не возвращается. Другими словами, при вызове асинхронной процедуры вызывающая сторона не получает результат немедленно. но вперечислитьПосле выпуска,Вызываемый* информирует вызывающего абонента через статус, уведомление или обрабатывает вызов с помощью функции обратного вызова.

Обычный пример — типичная модель асинхронного программирования, такая как Node.js: вы звоните владельцу книжного магазина и спрашиваете, есть ли у него книга «Распределенные системы». Если это механизм синхронной связи, владелец книжного магазина скажет: подождите, "Я проверю. "Нажмите", затем начните проверять и проверять, и когда проверка будет завершена (может быть 5 секунд, может быть день), он сообщит вам результат (вернет результат). Что касается механизма асинхронной связи, то владелец книжного магазина прямо сказал вам проверить его, после проверки позвонил вам, а затем сразу повесил трубку (не вернув результат). После проверки он возьмет на себя инициативу позвонить вам. Здесь босс перезванивает, «перезванивая».

Анализ. Прежде чем объяснять разницу между синхронным вводом-выводом и асинхронным вводом-выводом, сначала необходимо дать их определения. Определение, данное Стивенсом (на самом деле определение POSIX), таково:

A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;
An asynchronous I/O operation does not cause the requesting process to be blocked;

Разница между ними заключается в том, что синхронный ввод-вывод блокирует процесс при выполнении «операции ввода-вывода». В соответствии с этим определением вышеупомянутый* Блокирующий ввод-вывод, неблокирующий ввод-вывод и мультиплексирование ввода-вывода относятся к синхронному вводу-выводу.*Некоторые люди могут сказать, что неблокирующий ввод-вывод не блокируется. Здесь есть очень «хитрое» место: «операция ввода-вывода», упомянутая в определении, относится к реальной операции ввода-вывода, которой в примере является системный вызов recvfrom. При неблокирующем вводе-выводе выполняется системный вызов recvfrom, если данные ядра не готовы, процесс в это время не будет заблокирован. Однако, когда данные в ядре будут готовы, recvfrom скопирует данные из ядра в память пользователя.В это время процесс блокируется.В это время процесс блокируется.

Отличие асинхронного ввода-вывода заключается в том, что когда процесс инициирует операцию ввода-вывода, он напрямую возвращается и игнорирует ее до тех пор, пока ядро ​​не отправит сигнал, сообщающий процессу, что ввод-вывод завершен. Во время всего этого процесса процесс вообще не блокируется.

Пример изображения модели ИО

Наконец, давайте возьмем еще несколько неуместных примеров, чтобы проиллюстрировать эти четыре Модели IO: четыре человека A, B, C и D ловят рыбу: A использует самую старомодную удочку, поэтому ее нужно все время охранять. Подождите, пока рыба не сядет на крючок, а затем потяните удочку; удочка B имеет функцию, которая может отображать, поймана ли рыба на крючок, поэтому B болтает с ММ рядом с ним и через некоторое время проверяет, не попалась ли какая-нибудь рыба на крючок. , а если есть, быстро потяните за удочку; Удочка, используемая С, похожа на удочку Б, но он придумал хороший способ поставить несколько удочек одновременно, а затем держать их рядом. есть знак, что рыба на крючок, она подтянет соответствующую удочку; D — богатые люди, просто наймите кого-нибудь, чтобы помочь ему ловить рыбу, и как только этот человек поймает рыбу, отправьте текстовое сообщение D.

Механизм опроса Select/Poll/Epoll

select, poll, epoll по сути являются синхронным вводом-выводом, потому что все они должны отвечать за чтение и запись после того, как события чтения и записи будут готовы, то есть процесс чтения и записи заблокирован

Select/Poll/Epoll — все реализации мультиплексирования ввода-вывода.Как упоминалось выше, использование мультиплексирования ввода-вывода установит неблокирующий сокет, а затем поместит его в соответствующие списки мониторинга Select/Poll/Epoll.Затем их право Что такое механизмы контроля за поступлением данных в сокет? Как насчет эффективности? Какой способ лучше использовать для достижения мультиплексирования ввода-вывода? Ниже перечислены их соответствующие методы реализации, эффективность, преимущества и недостатки:

(1) Реализация select, poll должна непрерывно опрашивать все наборы fd самостоятельно, пока устройство не будет готово, в течение которого оно может чередоваться между спящим режимом и пробуждением несколько раз. И epoll действительно нужно вызвать epollwait постоянно опрашивает список готовности и может много раз переключаться между режимом сна и режимом пробуждения в течение этого периода, но когда устройство готово, оно вызывает функцию обратного вызова, помещает готовый fd в список готовых и просыпается в момент epollПроцесс, который засыпает в ожидании. Несмотря на то, что как sleep, так и alter, select и poll должны пройти через всю коллекцию fd, когда «бодрствуют», а epoll нужно только оценить, пуст ли список готовых файлов, когда «бодрствует», что экономит много процессорного времени. Это улучшение производительности, вызванное механизмом обратного вызова.

(2) Выберите, poll каждый вызов должен один раз скопировать набор fd из пользовательского режима в режим ядра и один раз повесить текущий в очередь ожидания устройства, в то время как epoll нужно скопировать только один раз и повесить текущий в очередь ожидания. (в начале epoll_wait обратите внимание, что очередь ожидания здесь — это не очередь ожидания устройства, а очередь ожидания, определенная внутри epoll). Это также может сэкономить много затрат.

обо мне

Если статья окажется для вас полезной, вы можете ее собрать и переслать, что станет для меня большим стимулом! Кроме того, вы можете обратить внимание на мой паблик [Code Farmer Fu Ge] (coder2025), я продолжу выкладывать оригинальные алгоритмы и базовые компьютерные статьи!