- Русские Блоги
- Режим ввода / вывода в Linux
- 1. Блочная модель ввода / вывода (блокировка ввода / вывода)
- 2. Неблокированный (неблокирующая модель ввода / вывода)
- 3. I / O мультиплексирование
- 4 、asynchronous I/O(Асинхронный ввод / вывод)
- 5. Резюме
- (1) Разница между блокирующим и неблокирующим
- (2) Разница между синхронным IO и асинхронным IO
- (3) Разница между неблокирующим IO и асинхронным IO
- Интеллектуальная рекомендация
- Используйте Maven для создания собственного архетипа скелета проекта (4)
- Станция интерпретации больших данных B пользуется популярностью среди гигантов района «призрачные животные» Цай Сюкуня.
- Вопрос A: Алгоритм 7-15: алгоритм кратчайшего пути Дейкстры
- Учебный дневник — перелистывание страниц
- Нулевое основание для отдыха-клиента
- Вам также может понравиться
- Подробно объясните, как новички используют sqlmap для выполнения инъекционных атак на базы данных mysql.
- Vue заметки сортируют, пусть вам начать с Vue.js:. 04_3 Сетевое приложение: AXIOS плюс Вью
- Шаблон алгоритма конной повозки
- 35 Line Code, чтобы получить метод исследования событий (ON)
- Образ докера: gitlab
- Блокирующий ввод вывод linux
- Содержание
- Термины: неблокирующий, асинхронный, событийный
- Блокирующий режим
- Неблокирующий режим (O_NONBLOCK)
- Мультиплексирование ввода-вывода (select, epoll, kqueue и т.д.)
- Как O_NONBLOCK сочетается с мультиплексером select
- Мультиплексер epoll в режиме edge-triggered
Русские Блоги
Режим ввода / вывода в Linux
дляОдин доступ к вводу-выводу (например, чтение)Данные сначала будут скопированы в буфер ядра операционной системы, а затем скопированы из буфера ядра операционной системы в буфер приложения и, наконец, переданы процессу. так,Когда происходит операция чтения, она проходит два этапа:
- В ожидании данных, чтобы быть готовым
- Копирование данных из ядра в процесс
Официально из-за этих двух этапов,Система Linux выпустила следующие пять решений для сетевого режима:
— блокировка ввода / вывода (блокировка ввода / вывода)
— неблокирующий ввод / вывод (неблокирующий ввод / вывод)
— мультиплексирование ввода / вывода (мультиплексирование ввода / вывода)
— управляемый сигналом ввод-вывод (управляемый сигналом ввод-вывод)
— асинхронный ввод-вывод (асинхронный ввод-вывод)
Примечание. Поскольку на практике не часто используется управляемый сигналом ввод-вывод, я упомяну только оставшиеся четыре модели ввода-вывода.
1. Блочная модель ввода / вывода (блокировка ввода / вывода)
Принципиальная схема блокировки ввода / вывода модели:
читать в качестве примера:
(1) Процесс инициирует чтение и выполняет системный вызов recvfrom;
(2) Ядро запускает первый этап, подготавливая данные (копирование с диска в буфер), данные, запрошенные процессом, не готовы в один клик, подготовка данных занимает время;
(3) В то же время процесс блокируется (процесс выбирает, блокировать или нет), ожидая получения данных;
(4) Пока данные не будут скопированы из ядра в пространство пользователя, ядро возвращает результат, и процесс разблокируется.
Другими словами,Ядро готовит данныес участиемДанные копируются из ядра на адрес памяти процессаОба процесса заблокированы.
2. Неблокированный (неблокирующая модель ввода / вывода)
Вы можете сделать это неблокирующим, установив сокет. При выполнении операции чтения на неблокирующем сокете процесс выглядит следующим образом:
(1) Когда пользовательский процесс выдает операцию чтения, если данные в ядре не готовы;
(2) Затем он не блокирует пользовательский процесс, но немедленно возвращает ошибку. С точки зрения пользовательского процесса, после того, как он инициирует операцию чтения, ему не нужно ждать, но он сразу же получает результат;
(3) Когда пользовательский процесс считает, что результатом является ошибка, он знает, что данные еще не готовы, поэтому он может снова отправить операцию чтения. Как только данные в ядре готовы и системный вызов пользовательского процесса получен снова;
(4) Затем он немедленно копирует данные в пользовательскую память и затем возвращается.
Поэтому характеристики неблокирующего ввода-выводаПользовательский процессвЭтап, на котором ядро готовит данныенеобходимостьПродолжать активно запрашивать данные。
3. I / O мультиплексирование
Мультиплексирование ввода / вывода фактически использует select, poll, epoll для мониторинга нескольких объектов io и уведомления пользовательского процесса об изменении объекта io (данные есть). Преимущество состоит в том, что один процесс может обрабатывать несколько сокетов. Конечно, конкретные различия будут обсуждаться позже. Теперь давайте рассмотрим процесс мультиплексирования ввода / вывода:
(1) Когда пользовательский процесс вызывает select, весь процесс будет заблокирован;
(2) В то же время ядро будет «контролировать» все сокеты, отвечающие за выборку;
(3) Когда данные в любом сокете будут готовы, select вернется;
(4) В это время пользовательский процесс снова вызывает операцию чтения, чтобы скопировать данные из ядра в пользовательский процесс.
Поэтому характеристика мультиплексирования ввода / вывода заключается в том, что процесс может одновременно ожидать несколько файловых дескрипторов через механизм, и эти файловые дескрипторы (дескрипторы сокетов)Любой из них входит в состояние готовности к чтению,select()Функция может вернуть。
Эта диаграмма мало чем отличается от диаграммы блокировки ввода-вывода, а на самом деле она еще хуже. Потому что здесь нужны два системных вызова (select и recvfrom), а блокировка ввода-вывода вызывает только один системный вызов (recvfrom). Однако преимущество использования select заключается в том, что он может обрабатывать несколько соединений одновременно.
так что,Если количество обработанных соединений не очень велико, использованиеВеб-сервер select / epoll не обязательноЧем с помощьюМногопоточность + блокировка ввода-выводаПроизводительность веб-сервера выше, а задержка может быть больше.
Преимущества select / epollНеМожно обрабатывать для одного соединенияБыстрееНо в состоянии справитьсяБольшеПодключение. )
В модели мультиплексирования ввода-вывода на практике для каждого сокета обычно устанавливается неблокирование, но, как показано на рисунке выше, весь пользовательский процесс фактически блокируется. Просто процесс выбирается этим функциональным блоком, а не сокетом IO для блокировки.
4 、asynchronous I/O(Асинхронный ввод / вывод)
Реальный асинхронный ввод-вывод очень мощный, процесс, вероятно, выглядит следующим образом:
(1) После того, как пользовательский процесс инициирует операцию чтения, он может немедленно начать делать другие вещи.
(2) С другой стороны, с точки зрения ядра, когда оно получает асинхронное чтение, оно сначала сразу же возвращается, поэтому для пользовательского процесса блок не будет сгенерирован.
(3) Затем ядро будет ожидать подготовки данных, а затем скопировать данные в пользовательскую память. Когда все это будет завершено, ядро отправит пользовательскому процессу сигнал о том, что операция чтения завершена.
5. Резюме
(1) Разница между блокирующим и неблокирующим
Вызов блокирующего ввода-вывода будет блокировать соответствующий процесс до завершения операции, а неблокирующий ввод-вывод немедленно вернется, когда ядро все еще готовит данные.
(2) Разница между синхронным IO и асинхронным IO
Прежде чем объяснить разницу между синхронным вводом-выводом и асинхронным вводом-выводом, необходимо дать определение обоим. Определение 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;
Разница между ними заключается в том, что синхронный ввод-вывод блокирует процесс при выполнении операции ввода-вывода. Согласно этому определению, вышеупомянутые блокирующие IO, неблокирующие IO и мультиплексирование IO все принадлежат синхронному IO.
Некоторые люди могут сказать, что неблокирующий ввод-вывод не блокируется. Здесь есть очень «хитрое» место. «Операция ввода-вывода» в определении относится к фактической операции ввода-вывода, которая в данном примере является системным вызовом recvfrom. Когда неблокирующий ввод-вывод выполняет системный вызов recvfrom,Если данные ядра не готовы, процесс не будет заблокирован в это время, Однако когдаКогда данные в ядре будут готовы, recvfrom скопирует данные из ядра в пользовательскую память, на этот раз процесс заблокированВ течение этого времени процесс заблокирован.
Асинхронный ввод-вывод отличается. Когда процесс инициирует операцию ввода-вывода, он возвращается напрямую и игнорирует его, пока ядро не отправит сигнал, чтобы сообщить процессу о завершении ввода-вывода. Во всем этом процессе процесс вообще не блокируется.
(3) Разница между неблокирующим IO и асинхронным IO
Можно обнаружить, что разница между неблокирующим вводом-выводом и асинхронным вводом-выводом все еще очевидна.
- В неблокирующих ввода-вывода, хотя процесс не будет заблокирован большую часть времени, но этоВсе еще требуют, чтобы процесс активно проверялИ когда данные будут готовы, вам также нужноПроцесс снова активно вызывает recvfrom для копирования данных в пользовательскую память。
- Асинхронный ввод-вывод совершенно другой. Это похоже на то, как пользовательский процесс передает всю операцию ввода-вывода другим (ядру) для завершения, а затем сообщает другим, когда они закончили. В течение этого периода,Пользовательский процесс не должен проверять состояние операций ввода-вывода и активно копировать данные.
Интеллектуальная рекомендация
Используйте Maven для создания собственного архетипа скелета проекта (4)
Один, базовое введение в Maven Во-вторых, скачайте и настройте Maven Три, настроить домашнее зеркало на Али В-четвертых, создайте содержимое скелета архетипа В-пятых, создайте проект через архетип 6. .
Станция интерпретации больших данных B пользуется популярностью среди гигантов района «призрачные животные» Цай Сюкуня.
Автор | Сюй Линь Ответственный редактор | Ху Вэйвэй Предисловие Недавно Цай Сюкунь отправил письмо юриста на станцию B. Содержание письма юриста показало, что «на станции B имеется большое кол.
Вопрос A: Алгоритм 7-15: алгоритм кратчайшего пути Дейкстры
Название Описание Во взвешенном ориентированном графе G для исходной точки v задача о кратчайшем пути от v до оставшихся вершин в G называется задачей кратчайшего пути с одной исходной точкой. Среди ш.
Учебный дневник — перелистывание страниц
Используйте плагин Layui.
Нулевое основание для отдыха-клиента
Предисловие: статья, обобщенная, когда я только что связался с тестом API, в дополнение к остальному клиенту этот инструмент сам, некоторые из мелких пониманий API, я надеюсь помочь тому же белую белу.
Вам также может понравиться
Подробно объясните, как новички используют sqlmap для выполнения инъекционных атак на базы данных mysql.
Шаг 1. Откройте для себя инъекцию Со мной все было нормально, когда я был свободен, я случайно нажал на чужой блог и обнаружил, что ссылка заканчивается на id, поэтому я проверил его вручную. Результа.
Vue заметки сортируют, пусть вам начать с Vue.js:. 04_3 Сетевое приложение: AXIOS плюс Вью
В предыдущем разделе мы ввели основное использование AXIOS, по сравнению с нативным Ajax, который при условии, что способ является более простым и, а сетевые данные теперь в состоянии получить его ров.
Шаблон алгоритма конной повозки
Блог гангстеров Тележки, запряженные лошадьми, используются для решения проблемы самой длинной подстроки палиндрома. Основное внимание уделяется подстрокам, а не подпоследовательностям. Если вы хотите.
35 Line Code, чтобы получить метод исследования событий (ON)
Об авторе: Чжу Сяою,Личный публичный номер: языковой класс большой кошки Эта проблема научит вас этой большой классе Cat.Как написать наиболее эффективное метод исследования событий с 35 Line R Code C.
Образ докера: gitlab
GitLab Docker images Both GitLab CE and EE are in Docker Hub: GitLab CE Docker image GitLab EE Docker image The GitLab Docker images are monolithic images of GitLab running all the necessary services .
Источник
Блокирующий ввод вывод linux
API для ввода-вывода в современных ОС позволяют обрабатывать сотни и тысячи одновременно открытых сетевых запросов либо файлов. Для этой цели не нужно создавать множество потоков — достаточно запустить специальный цикл событий на одном потоке, начав мультиплексирование ввода-вывода
Содержание
В этой статье мы покажем, что именно происходит, когда вы используете неблокирующий ввод-вывод. Мы рассмотрим:
- Что означают понятия “неблокирующий”, “асинхронный”, “событийный” для ввода-вывода
- Смысл добавления флага O_NONBLOCK для файловых дескрипторов через fcntl
- Почему неблокирующий ввод-вывод часто сочетается с мультиплексированием через select , epoll и kqueue
- Как неблокирующий режим ввода-вывода взаимодействует со средствам опроса дескрипторов, такими как epoll
Термины: неблокирующий, асинхронный, событийный
- “Асинхронный” буквально означает “не синхронный”. Например, отправка email асинхронная, потому что отправитель не ожидает получить ответ сразу же. В программировании “асинхронным” называют код, в котором компоненты посылают друг другу сообщения, не ожидая немедленного ответа.
- “Неблокирующий” — термин, чаще всего касающийся ввода-вывода. Он означает, что при вызове “неблокирующего” системного API управление сразу же будет возвращено программе, и она продолжит использовать свой квант процессорного времени. Обычные, простые в использовании системные вызовы блокирующие: они усыпляют вызывающий поток до тех пор, пока данные для ответа не будут готовы.
- “Событийный” означает, что компонент программы обрабатывает очередь событий с помощью цикла, а тем временем кто-либо добавляет события в очередь, формируя входные данные компонента, и забирает у него выходные данные.
Термины часто пересекаются. Например, протокол HTTP сам по себе синхронный (пакеты отправляются синхронно), но при наличии неблокирующего ввода-вывода программа, работающая с HTTP, может оставаться асинхронной, то есть успевать совершить какую-либо полезную работу между отправкой HTTP-запроса и получением ответа на него.
Блокирующий режим
По умолчанию все файловые дескрипторы в Unix-системах создаются в “блокирующем” режиме. Это означает, что системные вызовы для ввода-вывода, такие как read , write или connect , могут заблокировать выполнение программы вплоть до готовности результата операции. Легче всего понять это на примере чтения данных из потока stdin в консольной программе. Как только вы вызываете read для stdin, выполнение программы блокируется, пока данные не будут введены пользователем с клавиатуры и затем прочитаны системой. То же самое происходит при вызове функций стандартной библиотеки, таких как fread , getchar , std::getline , поскольку все они в конечном счёте используют системный вызов read . Если говорить конкретнее, ядро погружает процесс в спящее состояние, пока данные не станут доступны в псевдо-файле stdin. То же самое происходит и для любых других файловых дескрипторов. Например, если вы пытаетесь читать из TCP-сокета, вызов read заблокирует выполнение, пока другая сторона TCP-соединения не пришлёт ответные данные.
Блокировки — это проблема для всех программ, требующих конкурентного выполнения, поскольку заблокированные потоки процесса засыпают и не получают процессорное время. Существует два различных, но взаимодополняющих способа устранить блокировки:
- неблокирующий режим ввода-вывода
- мультиплексирование с помощью системного API, такого как select либо epoll
Эти решения часто применяются совместно, но предоставляют разные стратегии решения проблемы. Скоро мы узнаем разницу и выясним, почему их часто совмещают.
Неблокирующий режим (O_NONBLOCK)
Файловый дескриптор помещают в “неблокирующий” режим, добавляя флаг O_NONBLOCK к существующему набору флагов дескриптора с помощью fcntl :
С момента установки флага дескриптор становится неблокирующим. Любые системные вызовы для ввода-вывода, такие как read и write , в случае неготовности данных в момент вызова ранее могли бы вызвать блокировку, а теперь будут возвращать -1 и глобальную переменную errno устанавливать в EWOULDBLOCK , Это интересное изменение поведения, но само по себе мало полезное: оно лишь является базовым примитивом для построения эффективной системы ввода-вывода для множества файловых дескрипторов.
Допустим, требуется параллельно прочитать целиком данные из двух файловых дескрипторов. Это может быть достигнуто с помощью цикла, который проверяет наличие данных в каждом дескрипторе, а затем засыпает ненадолго перед новой проверкой:
Такой подход работает, но имеет свои минусы:
- Если данные приходят очень медленно, программа будет постоянно просыпаться и тратить впустую процессорное время
- Когда данные приходят, программа, возможно, не прочитает их сразу, т.к. выполнение приостановлено из-за nanosleep
- Увеличение интервала сна уменьшит бесполезные траты процессорного времени, но увеличит задержку обработки данных
- Увеличение числа файловых дескрипторов с таким же подходом к их обработке увеличит долю расходов на проверки наличия данных
Для решения этих проблем операционная система предоставляет мультиплексирование ввода-вывода.
Мультиплексирование ввода-вывода (select, epoll, kqueue и т.д.)
Существует несколько мультиплексирующих системных вызовов:
- Вызов select существует во всех POSIX-совместимых системах, включая Linux и MacOSX
- Группа вызовов epoll_* существует только на Linux
- Группа вызовов kqueue существует на FreeBSD и других *BSD
Все три варианта реализуют единый принцип: делегировать ядру задачу по отслеживанию прихода данных для операций чтения/записи над множеством файловых дескрипторов. Все варианты могут заблокировать выполнение, пока не произойдёт какого-либо события с одним из дескрипторов из указанного множества. Например, вы можете сообщить ядру ОС, что вас интересуют только события чтения для файлового дескриптора X, события чтения-записи для дескриптора Y, и только события записи — для Z.
Все мультиплексирующие системные вызовы, как правило, работают независимо от режима файлового дескриптора (блокирующего или неблокирующего). Программист может даже все файловые дескрипторы оставить блокирующими, и после select либо epoll возвращённые ими дескрипторы не будут блокировать выполнение при вызове read или write , потому что данные в них уже готовы. Есть важное исключение для epoll , о котором скажем далее.
Как O_NONBLOCK сочетается с мультиплексером select
Допустим, мы пишем простую программу-daemon, обслуживающее клиентские приложения через сокеты. Мы воспользуемся мультиплексером select и блокирующими файловыми дескрипторами. Для простоты предположим, что мы уже открыли файлы и добавили их в переменную read_fds , имеющую тип fd_set (то есть “набор файлов”). Ключевым элементом цикла событий, обрабатывающего файл, будет вызов select и дальнейшие вызовы read для каждого из дескрипторов в наборе.
Тип данных fd_set представляет просто массив файловых дескрипторов, где с каждым дескриптором связан ещё и флаг (0 или 1). Примерно так могло бы выглядеть объявление:
Функция select принимает несколько объектов fd_set . В простейшем случае мы передаём один fd_set с набором файлов для чтения, а select модифицирует их, проставляя флаг для тех дескрипторов, из которых можно читать данные. Также функция возвращает число готовых для обработки файлов. Далее с помощью макроса FD_ISSET(index, &set) можно проверить, установлен ли флаг, т.е. можно ли читать данные без блокировки.
Такой подход работает, но давайте предположим, что размер буфера buf очень маленький, а объём пакетов данных, читаемых из дескрипторов файлов, очень велик. Например, в примере выше размер buf всего 1024 байта, допустим что через сокеты приходят пакеты по 64КБ. Для обработки одного пакета потребуется 64 раза вызвать select , а затем 64 раза вызвать read . В итоге мы получим 128 системных вызовов, но каждый вызов приводит к одному переключению контекста между kernel и userspace, в итоге обработка пакета обходится дорого.
Можем ли мы уменьшить число вызовов select ? В идеале, для обработки одного пакета мы хотели бы вызвать select только один раз. Чтобы сделать это, потребуется перевести все файловые дескрипторы в неблокирующий режим. Ключевая идея — вызывать read в цикле до тех пор, пока вы не получите код ошибки EWOULDBLOCK , обозначающий отсутствие новых данных в момент вызова. Идея реализована в примере:
В этом примере при наличии буфера в 1024 байта и входящего пакета в 64КБ мы получим 66 системных вызовов: select будет вызван один раз, а read будет вызываться 64 раза без каких-либо ошибок, а 65-й раз вернёт ошибку EWOULDBLOCK .
Мультиплексер epoll в режиме edge-triggered
Группа вызовов epoll является наиболее развитым мультиплексером в ядре Linux и способна работать в двух режимах:
- level-triggered — похожий на select упрощённый режим, в котором файловый дескриптор возвращается, если остались непрочитанные данные
- если приложение прочитало только часть доступных данных, вызов epoll вернёт ему недопрочитанные дескрипторы
- edge-triggered — файловый дескриптор с событием возвращается только если с момента последнего возврата epoll произошли новые события (например, пришли новые данные)
- если приложение прочитало только часть доступных данных, в данном режиме оно всё равно будет заблокировано до прихода каких-либо новых данных
Чтобы глубже понять происходящее, рассмотрим схему работы epoll с точки зрения ядра. Допустим, приложение с помощью epoll начало мониторинг поступления данных для чтения из какого-либо файла. Для этого приложение вызывает epoll_wait и засыпает на этом вызове. Ядро хранит связь между ожидающими данных потоками и файловым дескриптором, который один или несколько потоков (или процессов) отслеживают. В случае поступления порции данных ядро обходит список ожидающих потоков и разблокирует их, что для потока выглядит как возврат из функции epoll_wait .
- В случае level-triggered режима вызов epoll_wait пройдёт по списку файловых дескрипторов и проверит, не соблюдается ли в данный момент условие, которое интересует приложение, что может привести к возврату из epoll_wait без какой-либо блокировки.
- В случае edge-triggered режима ядро пропускает такую проверку и усыпляет поток, пока не обнаружит событие прихода данных на одном из файловых дескрипторов, за которыми следит поток. Такой режим превращает epoll в мультиплексер с алгоритмической сложностью O(1): после прихода новых данных из файла ядро сразу же знает, какой процесс надо пробудить.
Для использования edge-triggered режима нужно поместить файловые дескрипторы в неблокирующий режим, и на каждой итерации вызывать read либо write до тех пор, пока они не установят код ошибки EWOULDBLOCK . Вызов epoll_wait будет более эффективным благодаря мгновенному засыпанию потока, и это крайне важно для программ с огромным числом конкурирующих файловых дескрипторов, например, для HTTP-серверов.
Источник