Автор: Мингуангту, инженер-разработчик серверной части Tencent IEG
Select/poll/epoll — это три технологии мультиплексирования ввода-вывода, предоставляемые серверами Linux для обработки сетевых запросов с высокой степенью параллелизма. Это общепринятая точка зрения, и понять ее основные принципы непросто.
Серверы Linux имеют три механизма обработки сетевых запросов: select, poll и epoll. В этой статье мы намерены подробно изучить принципы их реализации.
Не забудьте о землекопах, когда будете готовы. За последние две недели я потратил некоторое время на изучение статей Чжан Яньфея. Иллюстрация | Раскрыть в глубину epoll Как достичь IO мультиплексированный иДругие статьи и опубликовал книгу «Углубленное понимание Linux Network》, для блокировки ввод-вывод, мультиплексирование, epoll У меня есть определенное понимание принципов реализации и т. д.; статья Файги относительно ясно описывает базовую логику исходного кода, но иногда она недостаточно абстрактна, чтобы изложить суть вещей, и слишком много подробностей об исходном коде ядра, что повлечет за собой определенные затраты на обучение для читателей, эта статья надеется улучшиться в этом отношении.
В остальной части статьи в основном сделаны следующие выводы:
а) При вызове select вы попадете в ядро. В это время вам нужно скопировать fd_set из пространства пользователя в пространство ядра. В сценариях с высоким параллелизмом такая копия будет потреблять много ресурсов; epoll оптимизирован, чтобы не копировать)
б) После пробуждения процесса он получает данные, не зная, какие соединения готовы. Ему необходимо просмотреть каждый бит всего переданного fd_set, независимо от того, готовы ли они (epoll оптимизирован для асинхронного уведомления о событиях).
c) select возвращает только количество готовых файлов, а также те файлы, которые доступны для чтения и которые необходимо просмотреть (epoll оптимизирован так, чтобы возвращать только готовые дескрипторы файлов, без необходимости недопустимого обхода);
г) Количество файловых дескрипторов, которые можно отслеживать одновременно, слишком мало: 1024 или 2048 (опрос определяет ограничение длины на основе структуры связанного списка);
Чтобы поговорить о мультиплексировании ввода-вывода, лучше всего сначала четко проанализировать метод взаимодействия традиционной сети синхронного блокирования ввода-вывода.
Если клиент хочет отправить часть данных на сервер Linux, реализация языка C будет такой:
int main()
{
int fd = socket(); // Создайте структуру сокетов сетевой связи.
connect(fd, ...); // Установите TCP-соединение с сервером посредством трехэтапного рукопожатия.
send(fd, ...); // Запись данных приезжатьTCP соединение
close(fd); // Закрыть TCP-соединение
}
Сервер получает соединение клиента и данные, отправленные через следующий код C:
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 Модель блокировки ввода-вывода
В логике блокировки ввода-вывода существуют следующие три проблемы:
Подвести итог:один Второсортныйданныеприезжать Встреча продолжаетсяДва переключателя процесса,один ВторосортныйданныечитатьиметьЗаблокировано в двух местах,одиночный процессверноодинсоединять。
Для решения проблемы блокировки синхронизации IO проблема, операционная система обеспечивает неблокирующий recv() функция, эффект этой функции следующий: если никакие данные не достигают ядра с сетевой карты socket из Во время ожидания в очереди,Системный вызовбудет прямымвозвращаться,И даблокироватьиз подождем.
Если мы хотим сгенерировать неблокирующий сокет, следующий код на языке C будет выглядеть следующим образом:
// создать сокет
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 Модельрешено“Два переключателя процесса,Заблокировано в двух местах,одиночный процессверноодинсоединять”серединаиз“Заблокировано в двух местах”вопрос,Воля“Заблокировано в двух местах”стал“Блокировка”,но все еще существуетсуществовать“Два переключателя процесса,Блокировка,одиночный процессверноодинсоединять”извопрос。
решить“Два переключателя процесса,одиночный процессверноодинсоединять”извопрос,Сервер представил IO Технология мультиплексирования, обработка нескольких TCP соединение, не только уменьшает количество процессов обработки сетевых запросов сервером, но и не требует переключения процессов при поступлении данных каждого соединения. Процесс всегда может запускаться и обрабатывать только соединения с поступающими данными. все соединения, подлежащие мониторингу, являются: Если данные не поступают, процесс все равно перейдет в состояние блокировки до тех пор, пока он не будет разбужен функцией обратного вызова при поступлении данных по определенному соединению.
Модель мультиплексирования ввода-вывода показана на рисунке ниже:
Рисунок 1.4 Модель мультиплексирования ввода-вывода
Как вы можете видеть на картинке выше,Системный вызов select Функция блокирует выполнение и получает готовые данные по количеству подключений, затем вызывает recvfrom() Функция копирует данные, поступающие в пространство ядра, в пространство пользователя. Хотя оба этапа являются блокирующими, общая эффективность будет значительно повышена, поскольку будут обрабатываться только соединения с поступающими данными.
Здесь заблокировано IO Модельиз“Два переключателя процесса,Заблокировано в двух местах,одиночный процессверноодинсоединять”вопрос,проходитьнеблокирующий IO и Мультиплексированиетехнология,Только Что осталось“Блокировка”этотиндивидуальныйвопрос,Прямо сейчас Linux серверначальствопользовательский Процесс должен дождаться данных из пространства ядра, чтобы скопировать доступ в пространство пользователя. Если этот шаг также становится неблокирующим, то есть обрабатывается вызов. recvfrom Затем немедленно вернитесь, ядро само подготовит данные и скопирует их из пространства ядра в пространство пользователя, а затем notify уведомитьпользовательский процесс для чтения данных, затем да IO асинхронныйвызов,но,Linux Асинхронность не предусмотрена IO Реализация сети асинхронной в истинном смысле IO да Windows вниз IOCP(IO Доработка порта) модель здесь обсуждаться не будет.
Функция выбора, предоставляемая Linux, определяется следующим образом:
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:
#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 кодда:
#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.
и select Похоже, только да описание fd Способ сбора разный, опрос использовать pollfd структура, а не select из fd_set структура.
struct pollfd {
int fd; // хотетьмониториздескриптор файла
short events; // хотетьмониторизсобытие
short revents; // дескриптор События на самом деле произошли в файлеfd
};
Управляйте несколькими дескрипторами, а также выполняйте опрос, в соответствии с дескриптором статуса обрабатывается, но poll Нет максимального дескриптора файл Количество из лимита,Потому что он основан на хранилище связанных списков.。
select и poll Во внутреннем механизме существования нет большой разницы. по сравнению с select механизм, опрос Только да отменил максимальный дескриптор мониторинга ограничение на количество файлов, и принципиального решения этому нет select Безопасность это дачасмежду。
epoll даверно select и poll Улучшение, устранено «большие издержки производительности» и дескриптор файлаколичествонемного”этот两индивидуальный缺点,да Высочайшая производительность благодаря реализации мультиплексирования,Он может поддерживать наибольший объем параллелизма.
epoll из Возможностей:
1)использоватькрасное черное деревохранилищеодна порциядескриптор файласобирать,каждыйдескриптор Файл необходимо передать только один раз при добавлении существования, и пользователю не нужно вводить его каждый раз повторно;—— решено select середина fd_set Повторное копирование ядра из-за проблемы
2) Через асинхронно IO События Найти жилье Готовыйиздескриптор файл вместо да через опрос из;
3) Хранилище очередей готово и будет готово по требованию, без повторного обхода его;
epoll из базового использования:
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)
}
}
В основном он включает в себя три функции:
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 в основном содержит три поля:
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 пробуждает процесс с помощью двух последовательных функций обратного вызова, когда данные поступают в сокет.
от блокировки IO приезжать epoll из того, что понимает середина, мы можем посмотреть на приезд механизм функции обратного вызова пробужденияБудьте частоизиспользовать,至немногоиметьтри места:одиндаблокировать IO серединаданныеприезжатьдостигать socket из Во время ожидания в очереди передать функцию обратного вызов процесса пробуждения, Да epoll серединаданныеприезжатьдостигать socket из Во время ожидания в очереди пройти функцию обратного вызова ep_poll_callback попытаться найтиприезжать eventpoll серединакрасное черное деревоиз epitem узел и добавьте его в очередь готовности список rdllist, триDAпо функции обратного вызова default_wake_func будитьпользовательский процесс и будет rdllist перешел кпользовательский процесс,позволятьпользовательский Процесс Точное считывание данных 。отсередина Это может быть известно,этот Механизм обратного вызова может определить Кточныйиз Процесс уведомления для обработкиизсобытие,Нет необходимости каждый раз перебирать данные, чтобы проверить, были ли они достигнуты и каким процессом они должны быть обработаны.,Повышение эффективности программы,существоватьежедневноизразвитие бизнесасередина,Мы также можем извлечь уроки из этого механизма.
References
Иллюстрация | Раскрыть в глубину epoll Как достичь IO мультиплексированный!
Иллюстрация Linux Процесс получения сетевых пакетов
от linux Посмотреть исходный код socket изблокироватьинеблокирующий
Подробное объяснение Select, Poll и Epoll
Вы называете это дерьмовым мультиплексированием ввода-вывода?
Мультиплексирование ввода-вывода, подробное объяснение select / poll / epoll