Epoll de conmutación de E/S múltiples

Descripción básica 

        epoll es una versión mejorada de la interfaz IO multiplexada select/poll en Linux. Puede mejorar significativamente la utilización de la CPU del sistema del programa cuando solo hay una pequeña cantidad de conexiones activas en una gran cantidad de conexiones simultáneas, porque reutilizará el descriptor de archivo establecido en El resultado se pasa sin obligar al desarrollador a volver a preparar el conjunto de descriptores de archivo a monitorear cada vez antes de esperar el evento. Otra razón es que al obtener el evento, no es necesario atravesar todo el conjunto. De los descriptores monitoreados, solo aquellos establecidos por el kernel . Simplemente active el evento IO de forma asincrónica y agréguelo al conjunto de descriptores de la cola Listo.

        Además de proporcionar activación por nivel (Level Triggered) de eventos de IO como select/poll, epoll también proporciona activación por flanco (Edge Triggered), lo que hace posible que los programas de espacio de usuario almacenen en caché el estado de IO, reduzcan las llamadas a epoll_wait/epoll_pwait y mejorar la eficiencia de la aplicación. 

Tres funciones del modelo Epoll

https://www.bilibili.com/video/BV1iJ411S7UA?p=71&spm_id_from=pageDriver&vd_source=d239c7cf48aa4f74eccfa736c3122e65 https://www.bilibili.com/video/BV1iJ411S7UA?p=71&spm_id_from=pageDriver&vd_source=d239c 7cf48aa4f74eccfa736c3122e65

#include <sys/epoll.h>

int epoll_create(int size);
作用:创建一个epoll句柄,告诉他需要监听的数目(也可以理解成申请一片空间,用于存放监听的套接字)
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
作用:控制某个epoll监控的文件描述符上的事件:注册,修改、删除(也就是增添 删除 修改一个事件)
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
作用:监听红黑树上的事件,将产生动静的事件放在event这个数组内,

función epoll_create

Cree un identificador de epoll. El parámetro de tamaño se utiliza para indicarle al kernel la cantidad de descriptores de archivos a monitorear, que está relacionado con el tamaño de la memoria.

  • int epoll_create(int size);
    • Parámetro: Notificar al kernel para monitorear el tamaño fd. Es solo un valor recomendado y tiene algo que ver con el hardware. (Desde la versión 2.6.8 del kernel de Linux, el parámetro de tamaño se ha ignorado y solo se requiere que el tamaño sea mayor que 0)
    • Valor de retorno: Devuelve el identificador de epoll (fd)

función epoll_ctl

Controlar eventos en un descriptor de archivo monitoreado por epoll: registro, modificación y eliminación.

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *evento);

  • Parámetro 1: valor de retorno de int epfd:epoll_create()
  • Parámetro dos: int op: representa acción, representada por tres macros

                        EPOLL_CTL_ADD (Registrar un nuevo fd en epfd)
                        EPOLL_CTL_MOD (Modificar el evento de escucha de fd registrado)
                        EPOLL_CTL_DEL (Eliminar un fd de epfd)

  • Parámetro tres: objeto de operación int fd (socket)
  • Parámetro cuatro: estructura epoll_evevt* evevt; le dice al kernel los eventos que deben monitorearse               
结构体如下:
    struct epoll_event {
        uint32_t events; 宏定义读和写EPOLLIN读EPOLLOUT写
        epoll_data_t data; 联合体
    };

联合体如下:
    typedef union epoll_data {
        void *ptr;
        int fd;
        uint32_t u32;
        uint64_t u64;
    } epoll_data_t;
  • Valor de retorno: 0 se devuelve correctamente, -1 se devuelve sin éxito.

función epoll_wait

Espere a que ocurra un evento en el descriptor del archivo monitoreado, similar a una llamada select().

int epoll_wait (int epfd,struct epoll_event * eventos,int maxevents,int tiempo de espera)

  • Parámetro 1: valor de retorno de la función int epfd:epoll_create()
  • Parámetro dos: struct epoll_events* events se usa para devolver una matriz para procesar eventos (es decir, para almacenar eventos que generan movimiento)
  • Parámetro tres: int maxevents es el número máximo de eventos que se pueden generar al mismo tiempo, indicándole al kernel qué tan grandes son los eventos, este valor debe ser mayor que 0.
  • Parámetro cuatro: int timeout significa -1 es equivalente a bloquear , 0 es equivalente a no bloquear, tiempo de espera (unidad: milisegundos)
  • Valor de retorno: devuelve con éxito el número de eventos estáticos y dinámicos.

Explicación:  Producir movimiento significa

  1. Hay un nuevo cliente que necesita ser conectado o
  2. Un cliente conectado envió un mensaje

Epoll implementa ideas de transferencia IO multicanal 

https://www.bilibili.com/video/BV1iJ411S7UA?p=73&spm_id_from=pageDriver&vd_source=d239c7cf48aa4f74eccfa736c3122e65 https://www.bilibili.com/video/BV1iJ411S7UA?p=73&spm_id_from=pageDriver&vd_source=d239c 7cf48aa4f74eccfa736c3122e65

Código de caso

/* server.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <ctype.h>

#include "wrap.h"

#define MAXLINE 8192
#define SERV_PORT 8000

#define OPEN_MAX 5000

int main(int argc, char *argv[])
{
    int i, listenfd, connfd, sockfd;
    int  n, num = 0;
    ssize_t nready, efd, res;
    char buf[MAXLINE], str[INET_ADDRSTRLEN];
    socklen_t clilen;

    struct sockaddr_in cliaddr, servaddr;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);
	
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));      //端口复用
	
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
	
    Bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    Listen(listenfd, 20);

    efd = epoll_create(OPEN_MAX);               //创建epoll模型, efd指向红黑树根节点
    if (efd == -1)
        perr_exit("epoll_create error");

    struct epoll_event tep, ep[OPEN_MAX];       //tep: epoll_ctl参数  ep[] : epoll_wait参数

    tep.events = EPOLLIN; 
    tep.data.fd = listenfd;           //指定lfd的监听时间为"读"

    res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);    //将lfd及对应的结构体设置到树上,  efd可找到该树
    if (res == -1)
        perr_exit("epoll_ctl error");

    for ( ; ; ) {
        /*epoll为server阻塞监听事件, ep为struct epoll_event类型数组, OPEN_MAX为数组容量, -1表永久阻塞*/
        nready = epoll_wait(efd, ep, OPEN_MAX, -1); 
        if (nready == -1)
            perr_exit("epoll_wait error");

        for (i = 0; i < nready; i++) {
            if (!(ep[i].events & EPOLLIN))      //如果不是"读"事件, 继续循环
                continue;

            if (ep[i].data.fd == listenfd) {    //判断满足事件的fd是不是lfd            
                clilen = sizeof(cliaddr);
                connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);    //接受链接

                printf("received from %s at PORT %d\n", 
                        inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), 
                        ntohs(cliaddr.sin_port));
                printf("cfd %d---client %d\n", connfd, ++num);

                tep.events = EPOLLIN; tep.data.fd = connfd;
                res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);      //加入红黑树
                if (res == -1)
                    perr_exit("epoll_ctl error");

            } else {                                                    //不是lfd, 
                sockfd = ep[i].data.fd;
                n = Read(sockfd, buf, MAXLINE);

                if (n == 0) {                                           //读到0,说明客户端关闭链接
                    res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);  //将该文件描述符从红黑树摘除
                    if (res == -1)
                        perr_exit("epoll_ctl error");
                    Close(sockfd);                                      //关闭与该客户端的链接
                    printf("client[%d] closed connection\n", sockfd);

                } else if (n < 0) {                                     //出错
                    perror("read n < 0 error: ");
                    res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);  //摘除节点
                    Close(sockfd);

                } else {                                                //实际读到了字节数
                    for (i = 0; i < n; i++)
                        buf[i] = toupper(buf[i]);                       //转大写,写回给客户端

                    Write(STDOUT_FILENO, buf, n);
                    Writen(sockfd, buf, n);
                }
            }
        }
    }
    Close(listenfd);
    Close(efd);

    return 0;
}

Supongo que te gusta

Origin blog.csdn.net/weixin_43200943/article/details/130133177
Recomendado
Clasificación