Статьи о платформе: см. модель высокопроизводительной сети Linux IO+Reactor.

Java
Статьи о платформе: см. модель высокопроизводительной сети Linux IO+Reactor.

предисловие

Сетевой ввод-вывод можно понимать как поток данных в сети. Обычно мы устанавливаем канал TCP или UDP на основе сокета и удаленного конца, а затем читаем и записываем. С одним сокетом для эффективной обработки можно использовать один поток, однако, если это 10 000 подключений к сокету или больше, как мы можем добиться высокой производительности обработки?

  • Введение в основные понятия
  • Процесс чтения и записи сетевого ввода/вывода
  • Пять моделей сетевого ввода-вывода под Linux
  • Волна глубокого понимания мультиплексирования ввода-вывода
  • Модель реактора
  • Проакотр модель

Обратите внимание на официальный аккаунт и общайтесь вместе: Прокрадитесь вперед

Введение в основные понятия

  • Переключение процессов (потоков)
    • Все системы имеют возможность планировать процессы, которые могут приостановить текущий процесс и возобновить ранее приостановленные процессы.
  • Блокировка процесса (потока)
    • Работающий процесс иногда ожидает завершения выполнения других событий, таких как ожидание блокировки, запрос чтения и записи ввода-вывода; процесс ожидает автоматической блокировки процесса системой, и процесс не занимает ЦП в это время
  • файловый дескриптор
    • В Linux файловый дескриптор — это абстракция, используемая для выражения ссылки на файл, который является неотрицательным целым числом. Когда программа открывает существующий файл или создает новый файл, ядро ​​возвращает процессу дескриптор файла.
  • обработка сигналов линукс
    • Процесс Linux может принимать значение сигнала от системы или процесса, а затем запускать соответствующую функцию захвата в соответствии со значением сигнала; сигнал эквивалентен программному моделированию аппаратного прерывания.

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

Процесс чтения и записи сетевого ввода-вывода

  • Когда в пользовательском пространстве инициируется операция чтения сокета, это вызовет переключение контекста, и пользовательский процесс заблокируется (стадия R1), ожидая прихода потока сетевых данных, копируя его с сетевой карты в ядро. ; (стадия R2), а затем из буфера ядра в копию буфера пользовательского процесса. В этот момент процесс переключения возобновляется, и полученные данные обрабатываются.
  • Здесь мы называем первый этап операции чтения сокета псевдонимом R1, а второй этап называется R2.
  • Когда операция отправки сокета инициируется в пользовательском пространстве, происходит переключение контекста, и пользовательский процесс блокируется и ожидает (этап W1), чтобы скопировать данные из буфера пользовательского процесса в буфер отправки ядра (буфер сокета). Копирование данных завершено, и в это время возобновляется переключение процессов. только одна фаза блокировки

Linux пять моделей сетевого ввода-вывода

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

ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socket_t *fromlen);

  • Самой базовой моделью ввода-вывода является блокирующая модель ввода-вывода, которая также является самой простой моделью. Все операции выполняются последовательно
  • В модели блокирующего ввода-вывода приложение пользовательского пространства выполняет системный вызов (recvform), который блокирует приложение до тех пор, пока данные в буфере ядра не будут готовы и данные не будут скопированы из ядра в пользовательский процесс. Последний процесс пробуждается системой для обработки данных
  • На двух последовательных этапах R1 и R2 весь процесс блокируется

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

  • Неблокирующий ввод-вывод также является типом синхронного ввода-вывода. Он реализован на основе механизма опроса, при котором сокет открывается неблокирующим образом. То есть операция ввода-вывода не будет завершена немедленно, но операция ввода-вывода вернет код ошибки (EWOULDBLOCK), указывающий, что операция не завершена.
  • Опрос для проверки данных ядра и возврата EWOULDBLOCK, если данные не готовы. Процесс продолжает инициировать вызов recvfrom, конечно, вы можете сделать паузу, чтобы заняться другими делами.
  • Пока данные ядра не будут готовы, скопируйте данные в пространство пользователя, а затем процесс получит данные кода без ошибок, а затем выполнит обработку данных. Следует отметить, что в течение всего процесса копирования данных процесс все еще находится в заблокированном состоянии.
  • Процесс заблокирован на этапе R2.Хотя он не заблокирован на этапе R1, его необходимо постоянно опрашивать.

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

  • Как правило, в серверной службе будет большое количество подключений к сокетам.Если статус чтения и записи нескольких сокетов можно запросить одновременно, если какой-либо из них готов, он будет обработан, и эффективность будет намного выше. . Это «мультиплексирование ввода-вывода», мультиплексирование относится к нескольким сокетам, а мультиплексирование относится к мультиплексированию одного и того же процесса.
  • Linux предоставляет реализации мультиплексирования ввода-вывода, такие как select, poll, epoll и т. д.
  • выберите или опросите, epoll блокирует вызовы
  • В отличие от блокирующего ввода-вывода, select не ждет поступления всех данных сокета перед обработкой, а возобновляет пользовательский процесс для обработки, когда часть данных сокета готова. Как узнать, что некоторые данные готовы в ядре? Ответ: Пусть система справится с этим.
  • Процесс также блокируется на стадиях R1 и R2, но на стадии R1 есть хитрость: в многопроцессной и многопоточной среде программирования мы можем выделить только один процесс (поток) для блокировки вызова select, а другие темы можно освободить?

Управляемый сигналом ввод-вывод (SIGIO)

  • Должна быть предусмотрена функция захвата сигнала, связанная с сокетом; после инициирования вызова sigaction процесс может быть освобожден для решения других задач.
  • Когда данные будут готовы в ядре, процесс получит сигнал SIGIO, а затем прервется для запуска функции захвата сигнала, вызовет recvfrom для чтения данных из ядра в пользовательское пространство, а затем обработает данные
  • Видно, что пользовательский процесс не будет блокироваться на этапе R1, но R2 все равно будет блокироваться и ждать

Асинхронный ввод-вывод (функции серии POSIX aio_)

  • По сравнению с синхронным вводом-выводом, асинхронный ввод-вывод не будет блокировать текущий процесс после того, как пользовательский процесс инициирует системный вызов асинхронного чтения (aio_read), независимо от того, готовы ли данные буфера ядра; процесс может обрабатывать другую логику после возврата системного вызова aio_read
  • Когда данные сокета готовы в ядре, система напрямую копирует данные из ядра в пространство пользователя, а затем использует сигнал для уведомления пользовательского процесса.
  • Процесс не блокируется на двух этапах R1 и R2.

Фаза написания пяти моделей сетевого ввода-вывода

  • Хотя в приведенных выше пяти моделях ввода-вывода обсуждаются только операции чтения, процесс операций записи и операций чтения в пяти моделях ввода-вывода в основном одинаков. Есть только один этап блокировки, данные копируются из пользовательского пространства в буфер сокета ( W1 ), а когда данные буфера сокета отправляются на другой конец, это контролируется системой
    • 1) Блокировка и ожидание копирования всех данных в буфер сокета
    • 2) Написать отказ и немедленно вернуться
    • 3) Мультиплексирование, дополнительный пользовательский поток для опроса для записи, а затем уведомляет пользовательский поток для операций записи
    • 4) Управляемый сигналами, асинхронно уведомляющий пользовательские потоки для операций записи
    • 5) AIO, системный поток для завершения операции записи

Волна глубокого понимания мультиплексирования ввода-вывода

select

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • 1) Используйте copy_from_user для копирования fd_set из пространства пользователя в пространство ядра
  • 2) Зарегистрируйте функцию обратного вызова __pollwait
  • 3) Обходим все fd и вызываем соответствующий метод опроса (для сокета этот метод опроса — sock_poll, sock_poll будет вызывать tcp_poll, udp_poll или datagram_poll в зависимости от ситуации)
  • 4) В качестве примера возьмем tcp_poll, его основная реализация — __pollwait, которая является функцией обратного вызова, зарегистрированной выше.
  • 5) Основная работа __pollwait - повесить текущий (текущий процесс) в очередь ожидания устройства.Разные устройства имеют разные очереди ожидания.Для tcp_poll очередь ожидания sk->sk_sleep (обратите внимание, процесс зависает на ждать Очередь не означает, что процесс находится в спящем режиме). После того, как устройство получит доступное для чтения или записи сообщение, оно разбудит процесс, ожидающий перехода устройства в спящий режим в очереди, и текущий будет разбужен.
  • 6) Когда метод poll вернется, он вернет маску, описывающую, готовы ли операции чтения и записи, и присвоит значения fd_set согласно этой маске.
  • 7) Если все fds пройдены, а маска для чтения и записи не возвращена, будет вызвана функция schedule_timeout, чтобы перевести выбранный процесс (то есть текущий) в спящий режим. Когда драйвер устройства сможет читать и записывать свои собственные ресурсы, он разбудит процесс, ожидающий перехода в спящий режим в очереди.
  • 8) Если превышен определенный период тайм-аута (указанный тайм-аут), независимо от того, получает ли select доступный для чтения и записи fd, процесс, вызывающий select, будет снова разбужен для получения CPU, а затем продолжит выполнение логики программы.
  • 9) Скопируйте fd_set из пространства ядра в пространство пользователя

Недостатки выбора

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

epoll

int epoll_create(int size);  
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);  
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); 
  • Вызов epoll_create создаст новый в кеше ядра.красно-черное деревоОн используется для хранения сокета, отправленного epoll_ctl в будущем, а также создаст еще один.rdllist двусвязный списокИспользуется для хранения готовых событий. Когда вызывается epoll_wait, просто просмотрите данные этого двусвязного списка rdllist
  • Когда epoll_ctl добавляет, изменяет и удаляет события в объекте epoll, он работает в красно-черном дереве rbr, что очень быстро.
  • События, добавленные в epoll, установят отношения обратного вызова с устройством (например, сетевой картой).Когда соответствующее событие произойдет на устройстве, будет вызван метод обратного вызова, и событие будет добавлено в двусвязный список rdllist; этот метод обратного вызова в ядре называется ep_poll_callback.

Два режима запуска epoll

  • epoll имеет два режима триггера, EPOLLLT и EPOLLET, LT — режим по умолчанию, а ET — «высокоскоростной» режим (поддерживает только сокет без блокировки)
    • В режиме LT (горизонтальный триггер), пока в файловом дескрипторе есть данные для чтения,Каждый раз, когда epoll_wait будет запускать событие чтения
    • В режиме ET (запускаемый фронтом) при обнаружении события ввода-вывода дескриптор файла с уведомлением о событии будет получен путем вызова epoll_wait.Для дескриптора файла, если он доступен для чтения, дескриптор файла должен быть прочитан до тех пор, пока он пусто (или вернуть EWOULDBLOCK),В противном случае следующий epoll_wait не вызовет это событие.

Преимущества epoll перед select

  • Решите три недостатка select
    • К первому недостатку: Решение для epoll находится в функции epoll_ctl. Каждый раз, когда в дескриптор epoll регистрируется новое событие (укажите EPOLL_CTL_ADD в epoll_ctl), соответствующий fd будет копироваться в ядро, вместо того, чтобы дублировать все fd во время epoll_wait. epoll гарантирует, что каждый fd будет скопирован только один раз в течение всего процесса (epoll_wait не нужно копировать)
    • По второму недостатку: epoll определяет функцию обратного вызова для каждого fd.Когда устройство будет готово и разбудит ожидающих в очереди ожидания, будет вызвана эта функция обратного вызова, и эта функция обратного вызова добавит готовый fd в список готовых. Работа epoll_wait на самом деле состоит в том, чтобы проверить, есть ли готовые fd в этом списке готовых (нет необходимости проходить)
    • К третьему недостатку: epoll не имеет этого ограничения Верхний предел FD, который он поддерживает, это максимальное количество открытых файлов Это число, как правило, намного больше, чем 2048. Например, на машине с 1 ГБ памяти это около 100 000. Вообще говоря , этот номер связан с системой Память имеет значение
  • Высокая производительность epoll
    • epoll использует красно-черное дерево для хранения событий дескриптора файла, которые необходимо отслеживать, а epoll_ctl быстро добавляет, удаляет и изменяет.
    • epoll может получить готовый fd без обхода, просто верните готовый список напрямую
    • После linux2.6 используется технология mmap, больше не нужно копировать данные из ядра в пространство пользователя, нулевое копирование

Вопросы о модели ввода-вывода epoll, которая является синхронной и асинхронной

  • Определение концепции
    • Синхронная операция ввода-вывода: вызывает блокировку запрашивающего процесса до завершения операции ввода-вывода.
    • Асинхронная операция ввода-вывода: это не приводит к блокировке процесса запроса. Асинхронная обработка только обрабатывает уведомление после завершения операции ввода-вывода и не выполняет активное чтение и запись данных. Ядро системы завершает чтение и запись данных.
    • Блокировка, неблокировка: готовы ли данные, к которым должен получить доступ процесс/поток, и нужно ли процессу/потоку ждать
  • Концепция асинхронного ввода-вывода требует неблокирующих вызовов ввода-вывода. Как упоминалось ранее, операция ввода-вывода делится на два этапа: R1 ожидает готовности данных. R2 копирует данные из ядра в процесс. Хотя epoll принимает механизм mmap после ядра 2.6, так что его не нужно копировать на этапе R2, но он все равно блокируется на R1. Поэтому классифицируется как синхронный ввод-вывод.

Модель реактора

Основная идея Reactor заключается в регистрации всех событий ввода-вывода, подлежащих обработке, на центральном мультиплексоре ввода-вывода, в то время как основной поток/процесс блокируется на мультиплексоре; как только событие ввода-вывода поступает или готово, мультиплексор возвращает и распределяет соответствующие события ввода-вывода, зарегистрированные заранее, на соответствующие процессоры

Введение в связанные понятия:

  • событие: это состояние, например:прочитать готовое событиеОтносится к состоянию, в котором мы можем читать данные из ядра.
  • разделитель событий: Как правило, ожидание события будет передано в epoll и select, а приход события случайный и асинхронный, поэтому необходимо циклически вызывать epoll. как инкапсуляция epoll)
  • обработчик события: после того, как событие произошло, необходимо обработать процесс или поток.Этот обработчик является обработчиком события, который обычно отличается от разделителя событий потоком.

Общий поток реактора

  • 1) Приложение находится вразделитель событийрегистрсобытие готовности к чтению-записииЧтение и запись готового обработчика событий
  • 2) Разделитель событий ожидает, когда произойдет событие готовности к чтению-записи.
  • 3) Когда происходит событие готовности к чтению-записи, активируется разделитель событий, и разделитель вызывает обработчик события готовности к чтению-записи.
  • 4) Обработчик событий сначала считывает данные из ядра в пространство пользователя, а затем обрабатывает данные

Один поток + реактор

Многопоточность + реактор

Многопоточность + несколько реакторов

Общий поток модели Proactor

  • 1) Приложение прописано в разделителе событийпрочитать полное событиеипрочитать полный обработчик события, и отправьте асинхронный запрос на чтение в систему
  • 2) Разделитель событий ожидает завершения события чтения
  • 3) В процессе ожидания разделителя система использует параллельные потоки ядра для выполнения фактической операции чтения, копирует данные в буфер процесса и, наконец, информирует разделитель событий о завершении чтения.
  • 4) Разделитель событий слушаетпрочитать полное событие,активацияпрочитать полный обработчик события
  • 5) Обработчик события завершения чтения напрямую обрабатывает данные в буфере пользовательского процесса.

Разница между Proactor и Reactor

  • Proactor основан на концепции асинхронного ввода-вывода, а Reactor обычно основан на концепции мультиплексирования ввода-вывода.
  • Proactor не нужно копировать данные из ядра в пространство пользователя, этот шаг делает система

Добро пожаловать на ошибку в тексте

Справочная статья