Углубленное изучение принципа реализации мультиплексирования ввода-вывода select/poll/epoll.
Углубленное изучение принципа реализации мультиплексирования ввода-вывода select/poll/epoll.

Автор: Мингуангту, инженер-разработчик серверной части Tencent IEG

Select/poll/epoll — это три технологии мультиплексирования ввода-вывода, предоставляемые серверами Linux для обработки сетевых запросов с высокой степенью параллелизма. Это общепринятая точка зрения, и понять ее основные принципы непросто.

Серверы Linux имеют три механизма обработки сетевых запросов: select, poll и epoll. В этой статье мы намерены подробно изучить принципы их реализации.

Не забудьте о землекопах, когда будете готовы. За последние две недели я потратил некоторое время на изучение статей Чжан Яньфея. Иллюстрация | Раскрыть в глубину epoll Как достичь IO мультиплексированный иДругие статьи и опубликовал книгу «Углубленное понимание Linux Network》, для блокировки ввод-вывод, мультиплексирование, epoll У меня есть определенное понимание принципов реализации и т. д.; статья Файги относительно ясно описывает базовую логику исходного кода, но иногда она недостаточно абстрактна, чтобы изложить суть вещей, и слишком много подробностей об исходном коде ядра, что повлечет за собой определенные затраты на обучение для читателей, эта статья надеется улучшиться в этом отношении.

0. Заключение

В остальной части статьи в основном сделаны следующие выводы:

  1. Сервер хочет получить данные клиента,Чтобы создать структуру ядра сокета,В основном содержит две важные структуры данных.,(процесс) очередь ожидания(данные) очередь приема,socket существует процесс середина в виде файла, вы можете использовать файлы дескриптор fd Чтобы выразить, чтобы облегчить понимание, эту статью середина, socket Объект ядра ≈ fd дескриптор файла ≈ TCP соединять;
  2. блокировать IO из Основной логики: Сервер и клиент устанавливают соединение socket После этого сервер пользовательский процесспроходить recv Функция При получении данных, если данные не приходят, то при использовании пользовательского режима дескриптор процесса обработки ифункция обратного вызов будет инкапсулировать прибытие элемента ожидания процесса середина, добавить прибытие socket из очереди ожидания процесса середина, если данные по соединению достигают сетевой карты, сетевая карта пропустит данные через нее; DMA Копия контроллера перемещает память ядра из RingBuffer середина,и К CPU Выдайте жесткое прерывание, затем процессор Прервать процесс до ядра середина ksoftirqd Выдается сигнал мягкого прерывания, и ядро ​​прерывает процесс. ksoftirqd Преобразовать память ядра в RingBuffer серединаизданныев соответствии сдатаграммиз IP и номер порта, скопируйте его в соответствующий socket изData поступает в очередь середина и затем передается socket очередь ожидания изпроцесса серединаизфункция обратного вызов, пробуждение для обработки данных пользователем процесс;
  3. блокировать IO извопросда:один Второсортныйданныеприезжать Встреча продолжаетсяДва переключателя процесса,один ВторосортныйданныечитатьиметьЗаблокировано в двух местах,одиночный процессверноодинсоединять;
  4. неблокирующий IO Модельрешено“Два переключателя процесса,Заблокировано в двух местах,одиночный процессверноодинсоединять”серединаиз“Заблокировано в двух местах”вопрос,Воля“Заблокировано в двух местах”стал“Блокировка”,но все еще существуетсуществовать“Два переключателя процесса,Блокировка,одиночный процессверноодинсоединять”извопрос;
  5. использовать процессмонитор Несколько Соединятьз IO Мультиплексированиетехнологиярешено“Два переключателя процесса,Блокировка,одиночный процессверноодинсоединять” серединаиз“Два переключателя процесса,одиночный процессверноодинсоединять”,Что осталось“Блокировка”,Да Linux серединасинхронный IO Всегда будут проблемы, потому что Linux Асинхронность не предусмотрена IO выполнить;
  6. Linux из IO Мультиплексирование реализуется тремя способами: select, poll и epoll. выбирать извопросда:

а) При вызове select вы попадете в ядро. В это время вам нужно скопировать fd_set из пространства пользователя в пространство ядра. В сценариях с высоким параллелизмом такая копия будет потреблять много ресурсов; epoll оптимизирован, чтобы не копировать)

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

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

г) Количество файловых дескрипторов, которые можно отслеживать одновременно, слишком мало: 1024 или 2048 (опрос определяет ограничение длины на основе структуры связанного списка);

  1. poll Только на основе связанного списка из структуры, полученной с максимальным дескриптором ограничения файлов, вопросы, прочее select Проблема низкой производительности до сих пор не решена; epoll,решено select из Первые три недостатка;
  2. epoll из Принцип реализация выглядит сложной, но на самом деле она очень проста. Обратите внимание на два слова «функция». обратного вызоваизиспользовать:данныеприезжатьдостигать socket из Во время ожидания в очереди,проходитьФункция обратного вызова ep_poll_callback попытаться найтиприезжать eventpoll объектсерединакрасное черное деревоиз epitem узел и добавьте его в очередь готовности rdllist,Затемпроходитьфункция обратного вызова default_wake_function будитьпользовательский процесс и будет rdllist перешел кпользовательский процесс,позволятьпользовательский процесс Точное считывание готово socket изданные。этот Механизм обратного вызова может определить Кточныйиз Процесс уведомления для обработкиизсобытие,Нет необходимости каждый раз перебирать данные, чтобы проверить, были ли они достигнуты и каким процессом они должны быть обработаны.,Ежедневное развитие может извлечь уроки из этой идеи.

1. Как Linux обрабатывает сетевые запросы

1.1 Блокировка ввода-вывода

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

Если клиент хочет отправить часть данных на сервер Linux, реализация языка C будет такой:

Язык кода:javascript
копировать
int main()
{
     int fd = socket();      // Создайте структуру сокетов сетевой связи.
     connect(fd, ...);       // Установите TCP-соединение с сервером посредством трехэтапного рукопожатия.
     send(fd, ...);          // Запись данных приезжатьTCP соединение
     close(fd);              // Закрыть TCP-соединение
}

Сервер получает соединение клиента и данные, отправленные через следующий код C:

Язык кода:javascript
копировать
int main()
{
     fd = socket(...);        // Создайте структуру сокетов сетевой связи.
     bind(fd, ...);           // обязательностькоммуникационный порт
     listen(fd, 128);         // Контролируйте порт связи, чтобы определить, можно ли установить TCP-соединение.
     while(1) {
         connfd = accept(fd, ...);              // заблокировать Установить соединение
         int n = recv(connfd, buf, ...);        // блокироватьчитатьданные
         doSomeThing(buf);                      // Что делать с прочитанными данными о прибытии
         close(connfd);                         // Закройте соединение и дождитесь следующего соединения в цикле.
    }
}

Разверните детали обработки запроса на стороне сервера и получите процесс получения данных синхронной блокировки сетевого ввода-вывода, как показано на рисунке ниже:

Рисунок 1.1 Процесс получения данных синхронной блокировки сетевого ввода-вывода

Основные шаги:

1) Сервер проходит функция сокета()Падение в режим ядра socket системный вызов, эта функция ядра создает socket Объект ядра,Есть два основных важныхиз Структура,(процесс) очередь ожидания,и(данные) получить очередь,Чтобы облегчить понимание, текстовый процесс можно добавить перед очередью ожидания. На самом деле, точнее не добавлять его. То же самое справедливо и для очереди приема; очередь ожидания процесса хранит серверную часть. вызов;, полученные сетевой картой. socket данные, подлежащие обработке;

2) Процесс A вызов recv() Функция получает данные и вводит recvfrom() Системный вызовфункция,Обнаружить socket Когда в очереди ожидания данных нет данных, которые она хочет получить, процесс A даст ЦП, переходит в состояние блокировки, процесс A Дескриптор процесса и функция обратного вызова, используемая для его пробуждения. callback func Будет сформирована структура, называемая элементом очереди ожидания, и помещена в socket Очередь ожидания процесса;

3) Клиент отправляет данные на сетевую карту сервера;

4) Сетевая карта сначала скопирует данные, передаваемые из сети, в кольцевой буфер памяти RingBuffer через программу управления DMA;

5) Направление сетевой карты CPU 发出硬серединаперерыв

6) ЦП ЦП Обработка запросов сетевых устройств приводит к тому, что сообщения от других устройств, таких как мышь и клавиатура, не обрабатываются.,Вызовет регистрацию сетевого драйвера изсередины, функция обработки прерывания,Сделайте это простоодин Быстрая обработканазад Прервать процесс до ядра середина ksoftirqd 发出软серединаперерыв,Просто отпустите ЦП, процесс мягкого прерывания обрабатывает сложную и трудоемкую логику запросов сетевых устройств;

7)Ядро середина прерывает процесс ksoftirqd После получения сигнала мягкого прерывания сетевая карта скопирует данные в память в соответствии с пакетом данных. IP и номер порта, скопируйте его в соответствующий socket очередь приема;

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

9) Процесс A получать CPU После вернется к вызову прибытия. функция Recvfrom()часблокироватьизпозиция продолжает исполнение,этотчас Обнаружить socket Если в очереди ожидания пространства ядра есть данные, пространство ядра будет сохранено в состоянии ядра. socket очередь ожиданияизданныекопироватьприезжатьпользовательское пространство,Затемвернетсяприезжать Процесс выполнения пользовательского режимаиз Пользовательская программа,от И правдаиз Подниматьблокировать

пользовательский процесс A существоватьвызов recvfrom() При использовании системных функций существует два этапа ожидания: когда данные не готовы, процесс A жду ядра socket Подготовьте данные после того, как ядро ​​подготовит данные, процесс; A Продолжитьжду ядра Воля socket Ожидание копирования данных в очередь в собственный пользовательский буфер после того, как ядро ​​завершит копирование данных в пользовательский буфер, процесс; A Только тогда будет recvfrom() Системный вызовсерединавозвращаться,И снимем статус блокировать. Общий процесс выглядит следующим образом:

Рисунок 1.2 Модель блокировки ввода-вывода

В логике блокировки ввода-вывода существуют следующие три проблемы:

  1. Процесс существующего получения, скорее всего, будет прекращен путем блокирования,Вызвать переключение процесса;
  2. когда TCP Подключиться к серверу с помощью сетевой карты и с сетевой карты копироватьприезжать пространство ядра. socket Когда данные ожидают в очереди, процесс будет разбужен, и процесс снова переключится и станет доступным; процесспродолжить выполнение функция Системный вызов Recvfrom(), после копирования данных из пространства ядра в пользовательский буфер, пользователь Только процесс будет фактически обрабатывать данные, необходимые для проживания;
  3. Процесс может одновременно ожидать только одного соединения. Если имеется много параллелизма, потребуется много процессов;

Подвести итог:один Второсортныйданныеприезжать Встреча продолжаетсяДва переключателя процесса,один ВторосортныйданныечитатьиметьЗаблокировано в двух местах,одиночный процессверноодинсоединять

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

Для решения проблемы блокировки синхронизации IO проблема, операционная система обеспечивает неблокирующий recv() функция, эффект этой функции следующий: если никакие данные не достигают ядра с сетевой карты socket из Во время ожидания в очереди,Системный вызовбудет прямымвозвращаться,И даблокироватьиз подождем.

Если мы хотим сгенерировать неблокирующий сокет, следующий код на языке C будет выглядеть следующим образом:

Язык кода:javascript
копировать
// создать сокет
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
...
// Изменить сокет на неблокируемый
fcntl(sock_fd, F_SETFL, fdflags | O_NONBLOCK);
// connect
....
while(1)  {
    int recvlen = recv(sock_fd, recvbuf, RECV_BUF_SIZE) ;
    ......
}
...

Неблокирующая модель ввода-вывода показана на рисунке ниже:

Рисунок 1.3. Неблокирующая модель ввода-вывода

Из приведенного выше рисунка мы знаем, что неблокирующий IO, будет ждать поступления данных с сетевой карты socket Эта часть пространства ядра стала неблокирующей, пользователь процессвызов recvfrom() Запрос будет отправлен повторно, чтобы проверить, поступили ли данные в пространство ядра. Если они не поступили, они вернутся немедленно без блокировки. Однако когда данные достигли пространства ядра socket из После ожидания в очереди пользователь процесс Еще придется подождать функция Recvfrom() копирует данные из пространства ядра в пространство пользователя, Только тогда будет recvfrom() Системный вызовфункциясерединавозвращаться。

неблокирующий IO Модельрешено“Два переключателя процесса,Заблокировано в двух местах,одиночный процессверноодинсоединять”серединаиз“Заблокировано в двух местах”вопрос,Воля“Заблокировано в двух местах”стал“Блокировка”,но все еще существуетсуществовать“Два переключателя процесса,Блокировка,одиночный процессверноодинсоединять”извопрос。

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

решить“Два переключателя процесса,одиночный процессверноодинсоединять”извопрос,Сервер представил IO Технология мультиплексирования, обработка нескольких TCP соединение, не только уменьшает количество процессов обработки сетевых запросов сервером, но и не требует переключения процессов при поступлении данных каждого соединения. Процесс всегда может запускаться и обрабатывать только соединения с поступающими данными. все соединения, подлежащие мониторингу, являются: Если данные не поступают, процесс все равно перейдет в состояние блокировки до тех пор, пока он не будет разбужен функцией обратного вызова при поступлении данных по определенному соединению.

Модель мультиплексирования ввода-вывода показана на рисунке ниже:

Рисунок 1.4 Модель мультиплексирования ввода-вывода

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

Здесь заблокировано IO Модельиз“Два переключателя процесса,Заблокировано в двух местах,одиночный процессверноодинсоединять”вопрос,проходитьнеблокирующий IO и Мультиплексированиетехнология,Только Что осталось“Блокировка”этотиндивидуальныйвопрос,Прямо сейчас Linux серверначальствопользовательский Процесс должен дождаться данных из пространства ядра, чтобы скопировать доступ в пространство пользователя. Если этот шаг также становится неблокирующим, то есть обрабатывается вызов. recvfrom Затем немедленно вернитесь, ядро ​​само подготовит данные и скопирует их из пространства ядра в пространство пользователя, а затем notify уведомитьпользовательский процесс для чтения данных, затем да IO асинхронныйвызов,но,Linux Асинхронность не предусмотрена IO Реализация сети асинхронной в истинном смысле IO да Windows вниз IOCP(IO Доработка порта) модель здесь обсуждаться не будет.

2. Подробное объяснение принципов реализации select, poll и epoll.

2.1 выбор принципа реализации
выберите определение функции

Функция выбора, предоставляемая Linux, определяется следующим образом:

Язык кода:javascript
копировать
int select(
    int nfds,                     // Мониторинг файла Самая большая коллекциядескриптор файладобавлять1
    fd_set *readfds,              // Мониторинг прочитал данные прибытия дескриптора коллекция файлов, тип ссылки из параметра
    fd_set *writefds,             // Монитор записи данных коллекция файлов, тип ссылки из параметра
    fd_set *exceptfds,            // Отслеживать возникновение исключений вплоть до дескриптора коллекция файлов, тип ссылки из параметра
    struct timeval *timeout);     // Тайминг блокировать время мониторинга

readfds、writefds、errorfds также три дескриптора файла 집합.select будет проходить по лицевой стороне каждой коллекции nfds дескрипторы соответственно находят дескрипторы, которые можно читать, записывать и возникать ошибки, которые в совокупности называются «готовыми» дескрипторами. Соответствующий набор в трех ссылочных параметрах затем заменяется найденным подмножеством, возвращая количество всех готовых дескрипторов.

timeout Вызов представления параметра select Длительность блокировки. если все fd дескриптор файл не готов, процесс блокировкивызова, пока не будет готов определенный дескриптор, или блокирование не превысит настройку из timeout После этого вернитесь. если timeout Параметры установлены на NULL, блокируется на неопределенный срок, пока дескриптор не будет готов, если; timeout Параметры установлены на 0, вернется немедленно без блокировки.

файловый дескриптор fd

дескриптор файла (файл дескриптор)даа неотрицательное целое число,от 0 начинать. Дескриптор файла используется процессом для идентификации открытого файла. Линукс Все в нем является файлом.

Система поддерживает дескриптор для каждого процесса. файлаповерхность,Указывает, что процесс открывает файл из таблицы записей.,иДескриптор файла на самом деле является индексом этой таблицы.。Каждый процесс по умолчанию имеет 3 дескрипторы файлов: 0 (stdin)、1 (stdout)、2 (stderr)。

socket

Сокеты могут использоваться для связи между разными процессами на одном хосте или между разными хостами. Операционная система сопоставляет сокет файловому дескриптору процесса, и процесс может взаимодействовать с удаленным хостом, читая и записывая этот файловый дескриптор.

socket правила межпроцессного взаимодействия основаны на абстракции высокого уровня, в то время как fd Обеспечивает конкретную реализацию основного изда. розетка и fd да соответствует одному из. проходить socket Общение, фактически, через файлы дескриптор fd Чтение и запись файлов.

В этой статье для удобства понимания можно считать, что объект ядра сокета — файловый дескриптор fd — TCP-соединение.

набор файловых дескрипторов fd_set

Тип fd_set в параметре функции select представляет собой коллекцию файловых дескрипторов.

потому чтофайловый дескриптор fd да, один из 0 начинается с целого числа без знака, поэтому вы можете использовать fd_set Каждый двоичный бит представляет дескриптор файла. Кто-то 1, что указывает на то, что соответствующий файловый дескриптор готов. Например, предположим fd_set Длина 1 Байты, затем один fd_set Максимальная переменная может представлять 8 дескриптор файла. когда select возвращаться fd_set = 00010011 когда представляет дескриптор файла 1、2、5 Уже готов.

API fd_set

Использование fd_set включает следующие API:

Язык кода:javascript
копировать
#include <sys/select.h>
int FD_ZERO(int fd, fd_set *fdset);  // Воля fd_set Все локации 0
int FD_CLR(int fd, fd_set *fdset);   // Воля fd_set определенное место 0
int FD_SET(int fd, fd_set *fd_set);  // Воля fd_set определенное место 1
int FD_ISSET(int fd, fd_set *fdset); // Обнаружение fd_set Является ли определенный человек 1

Как использовать select для мониторинга нескольких соединений

Использование сервера select Мониторинг нескольких соединений C кодда:

Язык кода:javascript
копировать
#define MAXCLINE 5       // Количество очередей подключения
int fd[MAXCLINE];        // Подключить дескриптор файлаочередь

int main(void)
{
      sock_fd = socket(AF_INET,SOCK_STREAM,0)          // Установить связь между хостами socket Структура
      .....
      bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr);         // обязательностьsocketприезжатькогдабывший сервер
      listen(sock_fd, 5);  // монитор 5 TCP-соединения

      fd_set fdsr;         // дескриптор типа растрового изображения файласобирать,01100 Указывает, что нет.1 и 2 бита имеют данные приехать.
      int max;

      for(i = 0; i < 5; i++)
      {
          .....
          fd[i] = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);   // и 5 Клиенты создаются последовательно TCP Соединять будетсоединятьвставить fd дескриптор файлаочередь
      }

      while(1)               // циклмониторсоединятьначальствоиздата Нет места для проживания
      {
        FD_ZERO(&fdsr);      // верно fd_set Прямо сейчас bitmap введите для сброса,Прямо сейчас все сброшено на 0

        for(i = 0; i < 5; i++)
        {
             FD_SET(fd[i], &fdsr);      // ВоляхотетьмониторизTCPсоединятьверноотвечатьиздескриптор бит файласуществоватьизbitmapiz установлен в 1, например 0110010110 поверхностьтребоватьхотетьмонитор Нет. 1, 2, 5, 7, 8 дескрипторы файлаверноотвечатьиз TCP соединять
        }

        ret = select(max + 1, &fdsr, NULL, NULL, NULL);  // вызовselectсистемафункция Войдите в ядро, чтобы проверить, какое Соединятьзданныеприезжатьдостигать

        for(i=0;i<5;i++)
        {
            if(FD_ISSET(fd[i], &fdsr))      // fd_setсерединадля1из Расположениеповерхность Показыватьизсоединять,означает, что есть данные о прибытии,Можетпозволятьпользовательский процесс чтения
            {
                ret = Recv(fd[i], buf, sizeof(buf), 0);
                ......
            }
        }
  }

Из комментариев мы видим, что в процессе использования select Мониторинг нескольких соединений Основные шаги:

1)вызов socket() Функция установления связи между хостами socket Структура, связывание() обязательность socket К текущему серверу, прослушивайте() Монитор пять TCP соединять;

2)вызов accept() Функция создания и 5 клиент TCP Подключитесь и поместите дескриптор подключенного файла в fd очередь файловых дескрипторов;

3) Определить переменную fdsr типа fd_set;

4)вызов FD_ZERO, будет fdsr Все локации 0;

5)вызов FD_SET, будет fdsr Расположение нескольких файловых дескрипторов для прослушивания 1,поверхность Показыватьхотетьмониторэтот几индивидуальныйдескриптор файлаобратитесь к Кизсоединять;

6)вызов select() функционировать и будет fdsr Параметры, переданные в select;

7)select воля fdsr средняя готовность 1. Неготовое положение 0,возвращатьсяготовыйиздескриптор файлаизколичество;

8) Когда select возвращатьсяназад,вызов FD_ISSET Проверьте, какие биты 1,поверхность Показыватьверноотвечатьдескриптор файлаверноотвечатьиз Соединятьзданныеужеготовый,Можетвызов recv Функция считывает данные соединения.

Процесс выполнения выбора

в серверном процессе A При запуске соединение будет контролироваться socket дескриптор файлада 3, 4 и 5. Если данные не достигают сетевой карты по этим трем соединениям, процесс A даст ЦП переходит в состояние блокировки, и процесс воля A Дескриптор процесса и функция обратного вызова, используемые при пробуждении, состоят из элементов очереди ожидания, которые необходимо добавить в socket объект 3、4、5 Обратите внимание,что в это время процесс ожидает в очереди. select вызовчас,fdsr Набор файловых дескрипторов копируется из пространства пользователя в пространство ядра, как показано на следующем рисунке:

Рисунок 2.1. При запуске процесса выбора данные не достигают сетевой карты.

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

1) Записать сетевые данные в очередь приема данных соответствующего сокета;

2) Разбудите ожидающий процесс A в очереди и снова поместите процесс A в очередь выполнения ЦП;

Предполагаем соединение 3、5 На сетевую карту поступают данные. Обратите внимание, что в это время. select вызови конец, фдср Набор файловых дескрипторов копируется из пространства ядра в пространство пользователя:

Рисунок 2.2. Когда данные поступают в процесс выбора, процесс пробуждается через функцию обратного вызова для чтения данных.

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

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

1. Накладные расходы, связанные с высокой производительностью

1)вызов select попадет в ядро. В этом случае нужно изменить параметры в файле. fd_set Скопируйте из пространства пользователя в пространство ядра, выберите После выполнения вам все равно придется fd_set Копирование из пространства ядра обратно в пространство пользователя. В сценариях с высоким уровнем параллелизма такое копирование потребует много ресурсов (epoll; Оптимизировано, чтобы не копировать)

2) После пробуждения процесса,Не знаю, какиесоединятьужеготовый Прямо сейчасполучатьприезжать Понятноданные,нуждатьсяхотеть Пройти в обходизвсе fd_set из Все, готовы они или нет (epoll; Оптимизирован для асинхронного уведомления о событиях)

3)select Остаётся только количество готовых файлов, и какие файлы читабельны нужно просмотреть (epoll; Оптимизирован только для готовых дескрипторов файл, не нужно делать инвалидный обход)

2. Количество файловых дескрипторов, которые можно отслеживать одновременно, слишком мало. ограничено sizeof(fd_set) Из-за размера существование определяется при компиляции ядра и не может быть изменено. Генерал да 32 битовая операционная системада 1024,64 Да 2048。(poll、epoll Оптимизирован для адаптации к методу связанного списка)

Второй недостаток решается с помощью опроса, а первый недостаток низкой производительности решается с помощью epoll.

2.2 Принцип реализации опроса

и select Похоже, только да описание fd Способ сбора разный, опрос использовать pollfd структура, а не select из fd_set структура.

Язык кода:javascript
копировать
struct pollfd {
    int fd;           // хотетьмониториздескриптор файла
    short events;     // хотетьмониторизсобытие
    short revents;    // дескриптор События на самом деле произошли в файлеfd
};

Управляйте несколькими дескрипторами, а также выполняйте опрос, в соответствии с дескриптором статуса обрабатывается, но poll Нет максимального дескриптора файл Количество из лимита,Потому что он основан на хранилище связанных списков.

select и poll Во внутреннем механизме существования нет большой разницы. по сравнению с select механизм, опрос Только да отменил максимальный дескриптор мониторинга ограничение на количество файлов, и принципиального решения этому нет select Безопасность это дачасмежду。

2.3 принцип реализации epoll

epoll даверно select и poll Улучшение, устранено «большие издержки производительности» и дескриптор файлаколичествонемного”этот两индивидуальный缺点,да Высочайшая производительность благодаря реализации мультиплексирования,Он может поддерживать наибольший объем параллелизма.

epoll из Возможностей:

1)использоватькрасное черное деревохранилищеодна порциядескриптор файласобирать,каждыйдескриптор Файл необходимо передать только один раз при добавлении существования, и пользователю не нужно вводить его каждый раз повторно;—— решено select середина fd_set Повторное копирование ядра из-за проблемы

2) Через асинхронно IO События Найти жилье Готовыйиздескриптор файл вместо да через опрос из;

3) Хранилище очередей готово и будет готово по требованию, без повторного обхода его;

epoll из базового использования:

Язык кода:javascript
копировать
int main(void)
{
      struct epoll_event events[5];
      int epfd = epoll_create(10);         // Создайте epoll объект
      ......
      for(i = 0; i < 5; i++)
      {
          static struct epoll_event ev;
          .....
          ev.data.fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
          ev.events = EPOLLIN;
          epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev);  // К epoll объектсерединадобавлятьдобавлятьхотетьуправлятьизсоединять
      }

      while(1)
      {
         nfds = epoll_wait(epfd, events, 5, 10000);   // Ждем своего руководстваизсоединятьначальствоиз IO событие

         for(i=0; i<nfds; i++)
         {
             ......
             read(events[i].data.fd, buff, MAXBUF)
         }
  }

В основном он включает в себя три функции:

Язык кода:javascript
копировать
int epoll_create(int size);   // Создайте eventpoll Объект ядра

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);   // Волясоединятьприезжатьsocketобъектдобавить в eventpoll объектначальство,epoll_eventдахотетьмониторизсобытие

int epoll_wait(int epfd, struct epoll_event *events,
               int maxevents, int timeout);      // ждатьсоединять socket издата Нет места для проживания

epoll_create

epoll_create Функция создаст struct eventpoll из Объект ядра,Похоже на сокет,Свяжите это с прибытием, когда процесс будет открыт список файлов середина.

eventpoll в основном содержит три поля:

Язык кода:javascript
копировать
struct eventpoll {
 wait_queue_head_t wq;      // Список очереди ожидания, хранение заблокированных процессов

 struct list_head rdllist;  // Готовые данные файл положу сюда приезжать

 struct rb_root rbr;        // красное черное дерево,управлятьпользовательский Добавить в раздел «Обработкавсе» socket соединять
        ......
}

wq: очередь ожидания,Если передний процесс не имеет данных для обработки,волякогдадескриптор предварительной обработкиифункция обратного вызова default_wake_functon Создайте элемент очереди ожидания и поместите его в текущую wq вернообращатьсяочередь,Когда данные программного середина будут готовы, они пройдут через wq найти засор в epoll объект пользовательского процесса.

rbr:одно деревокрасное черное дерево,управлятьпользовательский Добавить в раздел «Обработкавсе» socket соединять.

rdllist:готовыйиздескрипторизцепьповерхность。когдаиметьизсоединятьданныеготовыйизчасждать,Ядроволяготовыйизсоединятьпомещатьприезжать rdllist в связанном списке. Таким образом, процессу приложения достаточно оценить связанный список, чтобы найти готовый процесс, без необходимости проходить все дерево.

eventpoll Структура такая, как показано на рисунке. 2.3 Показано:

Рисунок 2.3 eventpollобъектизструктура

epoll_ctl

epoll_ctl Функция в основном отвечает за установление сервера и клиента. socket Подключиться, зарегистрировавшись на eventpoll В объекте будут сделаны три вещи:

1) Создайте epitem объект, в основном содержит два поля, хранящиеся отдельно socket fd Прямо сейчас Подключить дескриптор файла,и Принадлежностьиз eventpoll указатель объекта;

2)Воляодининдивидуальныйданныеприезжатьдостигатьчасиспользоватьприезжатьизфункция обратного вызовадобавить в socket изпроцессочередь ожиданиясередина,Уведомление,и Нет. 1.1 Фестиваль изблокировать IO Режим другой, добавьте сюда socket структура очереди ожидания изпроцесса середина, только функция обратного вызова,Дескриптор процесса не установлен,потому чтодлясуществовать epoll середина,событие daput процесса существуетpoll из очереди ожидания середина,Ожидание пробуждения функцией epoll_wait,и Нетдапомещатьсуществовать socket очередь ожидания изпроцесса середина;

3) Общие 1) Шаг создания из epitem объектвставлятькрасное черное дерево;

Рисунок 2.4. Результаты выполнения epoll_ctl

epoll_wait

epoll_wait Функция:Действие относительно простое, проверьте eventpoll объектизготовыйизсоединять rdllist Есть ли данные о прибытии на да, если нет, добавьте когда из дескриптора процесса В очередь ожидания добавьте приезжать eventpoll Процесс «из» ожидает в очереди, а затем процесс «блокироватькогда» ждет прибытия данных через функцию обратного вызов просыпается.

когда eventpoll мониторизсоединятьначальствоиметьданныеприезжатьдостигатьчас,проходить下面几индивидуальный步骤будитьверноотвечатьизпроцесс处理данные:

1)socket из Очередь приема данных имеет прибывающие данные, и они будут ожидать в очереди обработки из Функция обратного вызова ep_poll_callback будитькрасное черное деревосерединаизузел epitem;

2)ep_poll_callback функция Воляиметьданныеприезжатьдостигатьиз epitem добавить в eventpoll очередь объектовизготово rdllist середина;

3)ep_poll_callback проверка функции eventpoll объективизировать, есть ли какие-либо ожидающие элементы в очереди ожидания процесса, с помощью функции обратного вызова default_wake_func Разбудите этот процесс и выполните обработку данных;

4) После того, как процесс проснется, продолжаем epoll_wait Когда код приостанавливается и выполнение продолжается, поместите rdlist серединаготовыйизсобытиевозвращаться Даватьпользовательский процесс,позволятьпользовательский процессвызов recv достигли ядра socket Очередь ожидания копирования данных для использования пользовательского пространства.

Рисунок 2.5. epoll_wait пробуждает процесс с помощью двух последовательных функций обратного вызова, когда данные поступают в сокет.

3. Резюме

от блокировки IO приезжать epoll из того, что понимает середина, мы можем посмотреть на приезд механизм функции обратного вызова пробужденияБудьте частоизиспользовать,至немногоиметьтри места:одиндаблокировать IO серединаданныеприезжатьдостигать socket из Во время ожидания в очереди передать функцию обратного вызов процесса пробуждения, Да epoll серединаданныеприезжатьдостигать socket из Во время ожидания в очереди пройти функцию обратного вызова ep_poll_callback попытаться найтиприезжать eventpoll серединакрасное черное деревоиз epitem узел и добавьте его в очередь готовности список rdllist, триDAпо функции обратного вызова default_wake_func будитьпользовательский процесс и будет rdllist перешел кпользовательский процесс,позволятьпользовательский Процесс Точное считывание данных 。отсередина Это может быть известно,этот Механизм обратного вызова может определить Кточныйиз Процесс уведомления для обработкиизсобытие,Нет необходимости каждый раз перебирать данные, чтобы проверить, были ли они достигнуты и каким процессом они должны быть обработаны.,Повышение эффективности программы,существоватьежедневноизразвитие бизнесасередина,Мы также можем извлечь уроки из этого механизма.

References

Иллюстрация | Раскрыть в глубину epoll Как достичь IO мультиплексированный!

Иллюстрация Linux Процесс получения сетевых пакетов

Иллюстрация | Глубоко понимать камни преткновения на пути к развитию высокопроизводительной сети. - сеть синхронной блокировки IO

от linux Посмотреть исходный код socket изблокироватьинеблокирующий

Подробное объяснение Select, Poll и Epoll

Вы называете это дерьмовым мультиплексированием ввода-вывода?

Мультиплексирование ввода-вывода, подробное объяснение select / poll / epoll

Dahua Select, Опрос, Epoll

IO Полное решение основных принципов мультиплексирования.,select,poll,epoll,socket,Система середина сломана,планирование процессов,Системный вызов

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose