предисловие
Сетевой ввод-вывод можно понимать как поток данных в сети. Обычно мы устанавливаем канал 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 не нужно копировать данные из ядра в пространство пользователя, этот шаг делает система