Modelo IO (IO con bloqueo, IO sin bloqueo, IO controlado por señal, IO asíncrono, IO multiplexado)

1. Cinco modelos IO típicos

  • IO de bloqueo, IO sin bloqueo, IO controlado por señal, IO asíncrono, modelo de multiplexación.
  • El proceso de IO:
    inicie una llamada de IO, espere a que la condición de IO esté lista y luego copie los datos al búfer para procesarlos: espere / copie.
1. Bloqueo de E/S:
  • Para completar el IO, se inicia una llamada IO. Si no se cumplen las condiciones de llamada en este momento, esperará hasta que se cumplan las condiciones y se complete la llamada IO.
    Bloqueo de E/S
  • El proceso es muy simple: solo después de completar un IO se puede realizar la siguiente llamada de IO. Los recursos no se utilizan por completo y están en estado de espera la mayor parte del tiempo.
2. IO sin bloqueo:
  • Para completar el IO, se inicia una llamada. Si las condiciones de IO no se cumplen actualmente, se devolverá un error de inmediato. (Por lo general, regresa para realizar otras operaciones e iniciar llamadas IO nuevamente).
    E/S sin bloqueo
  • En comparación con el bloqueo de IO, la utilización de recursos es más completa, pero las operaciones de IO no son en tiempo real.
3. IO impulsada por señal:
  • Defina el método de procesamiento de señal IO, realice operaciones de IO en el método de procesamiento, notifique el proceso con la señal IO listo y realice llamadas IO cuando el IO esté listo.
    E/S impulsada por señal
  • Para IO impulsada por señales, IO es más en tiempo real y utiliza todos los recursos, pero el proceso de llamada es complicado.
4. IO asincrónica:
  • Dígale al sistema operativo a través de llamadas IO asincrónicas qué datos IO se copian y dónde, y el sistema operativo completa el proceso de espera y el proceso de copia.
    E/S asincrónicas
  • Los recursos se utilizan al máximo y el proceso es más complejo.

Nota:
①Bloqueo: para completar una función, inicie una llamada. Si las condiciones de finalización no se cumplen actualmente, esperará para siempre.
② Sin bloqueo: para completar una función, inicie una llamada. Si las condiciones de finalización no se cumplen actualmente, se devolverá un error directamente.
③La diferencia entre bloqueo y no bloqueo: si se debe esperar cuando se inicia la llamada y no se completa.
④Sincronización: proceso de procesamiento, procesamiento secuencial, después de que se completa uno, se completa el otro, todas las funciones las completa el proceso mismo.
⑤Asincrónico: en el proceso de procesamiento, el orden es incierto porque las funciones las completa el sistema operativo.
⑥Bloqueo asincrónico: otros completan la función y la llamada espera a que otros la completen.
⑦ Sin bloqueo asíncrono: otros completan la función y la llamada regresa inmediatamente.

2. E/S múltiplex

  • El monitoreo centralizado de eventos de IO para una gran cantidad de descriptores puede decirle al programador/proceso qué descriptores están listos para qué eventos. En este momento, el programador/proceso puede responder directamente a los descriptores de los eventos correspondientes que están listos para evitar Realizar operaciones de IO sobre operadores que no están listos resulta en una reducción de la eficiencia/bloqueo del flujo del programa.
  • Eventos IO: eventos legibles, eventos grabables, eventos anormales;
  • Modelo IO de multiplexación: select/poll/epoll; utilizado para monitorear descriptores.
1.seleccione el modelo:
(1) Proceso de operación :

① El programador define un conjunto de descriptores para un determinado evento (conjunto de descriptores para eventos legibles/conjunto de descriptores para eventos de escritura/conjunto de descriptores para eventos de excepción), inicializa y borra el conjunto y determina qué descriptor se preocupa por qué evento. Este descriptor se agrega al conjunto de descriptores del evento correspondiente.
② Copie la colección en el kernel para su monitoreo. El principio de monitoreo es el sondeo y el juicio transversal. Listo para eventos legibles: el tamaño de los datos en el búfer de recepción es mayor que la marca de nivel bajo (estándar cuantificado; generalmente el valor predeterminado es un byte); Listo para eventos grabables: el tamaño del espacio restante en el búfer de envío es mayor que la marca de nivel bajo (estándar cuantificado; generalmente el valor predeterminado es un byte) Generalmente el valor predeterminado es un byte); preparación para eventos de excepción: si el descriptor genera una excepción.
③La llamada de monitoreo regresa, lo que indica un error de monitoreo/descriptor listo/tiempo de espera de monitoreo; y cuando la llamada regresa, los descriptores no listos en el conjunto de descriptores de monitoreo de eventos se eliminan del conjunto (solo se retiene listo en el descriptor del conjunto). Debido a que la colección se modifica al regresar, deberá agregar operadores a la colección nuevamente la próxima vez que realice el monitoreo.
④El programador sondea para determinar en qué conjunto se encuentra todavía el descriptor, determina si el descriptor está listo para un determinado evento y luego realiza la operación correspondiente al evento; la selección no regresa directamente al descriptor listo del usuario para operación directa, pero devuelve un conjunto de descriptores virtuales, por lo que el programador debe emitir un juicio.

(2) Operación del código :

① Defina el conjunto - struct fd_set - El miembro es una matriz, utilizada como un mapa de bits binario - agregar un descriptor significa establecer la posición del bit correspondiente al valor del descriptor en 1, por lo que la cantidad de descriptores que seclect puede monitorear depende de los bits de el mapa de bits binario El número de bits: el número de bits depende de la macro (_FD_SETSIZE, el valor predeterminado es 1024);

  •   void FD_ZERO(fd_set* set);—初始化清空集合。
    
  •   void FD_SET(int fd, fd_set* set);—将操作符fd增加到set集合中。
    

②Iniciar interfaz de llamada:

  •   int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timeval* timeout);
      nfds:当前监控的集合中最大的描述符+1;减少遍历次数。
      readfds/writefd/exceptfds:可读/可写/异常三种事件的集合。
      timeout:时间的结构体。struct{tv_sec;tv_usec;},通过这个事件决定select阻塞/非阻塞/限制超时阻塞;
      	-若timeout为NULL,则表示阻塞监控,直到描述符就绪,或者监控出错才会返回。
      	-若timeout中的成员数据为0,则表示非阻塞,监控的时候若没有操作符就绪,则就立即超时返回。
      	-若timeout中成员数据不为0,则在指定时间内,没有就虚则超时返回。
      	返回值:返回值大于0表示就绪的描述符个数;返回值等于0表示没有棉袄舒服就绪,超时返回;返回值小于0表示监控出错。	
    

③La llamada devuelve y devuelve al programador un conjunto de descriptores listos. El programador se desvía del juicio de qué descriptor todavía está en el conjunto y cuál es el evento que está listo.

  •   int FD_ISSET(int fd, fd_set* set);—判断fd描述符是否在集合中;
    
  • Debido a que la colección se modificará cuando regrese la selección, el descriptor debe volver a agregarse cada vez que se monitoree.

④Si ya no desea monitorear el descriptor, elimine el descriptor fd del conjunto

  •   void FD_CLR(int fd, fd_set* set);--从set集合中删除描述符fd;
    
(3) Seleccione para monitorear la entrada estándar:
                                                                                                                                                                                                                                                  
   #include<stdio.h>
   #include<unistd.h>
   #include<string.h>
   #include<fcntl.h>
   #include<sys/select.h>
   
   int main()
   {
    
    
     // 对标准输入进行监控
    // 1.定义指定事件的集合
    fd_set rfds;
    while(1)
    {
    
    
      printf("开始监控\n");
      //selent(maxfsd+1,可读集合,可写集合,异常集合,超时时间)
      struct timeval tv;
      tv.tv_sec = 3;
      tv.tv_usec = 0;
      FD_ZERO(&rfds);//初始化清空集合
      FD_SET(0,&rfds);// 将0号描述符添加到集合中
      int res = select(0+1,&rfds,NULL,NULL,&tv);
      if(res < 0){
    
    
        perror("select error");
        return -1;
      }else if(res == 0){
    
    
        printf("wait timeout\n");
        continue;
      }
      if(FD_ISSET(0,&rfds)){
    
    //判断描述符是否在集合中判断是否就绪了事件
        printf("准备从标准输入读取数据:...\n");
        char buf[1024] = {
    
    0};
        int ret = read(0,buf,1023);
        if(ret<0)
        {
    
    
          perror("read error");
          FD_CLR(0,&rfds);//移除描述符从集合中
          return -1;
        }
        printf("read buf:[%s]\n",buf);
      }
    }
    return 0;
  } 
(4) Análisis de ventajas y desventajas:
  • Desventajas :
    ① Select tiene un límite de número máximo para monitorear descriptores, y el límite superior depende de la macro - _FD - SETSIZE, con un tamaño predeterminado de 1024; ② El
    monitoreo en el kernel se logra mediante el juicio transversal de sondeo y el rendimiento aumentará con el descriptor Aumentar y disminuir
    ③ solo puede devolver el conjunto listo, y el proceso necesita atravesar el conjunto para saber qué descriptor está listo para qué evento.
    ④Cada monitoreo requiere volver a agregar descriptores a la colección, y cada monitoreo requiere volver a copiar la colección al kernel.
  • Ventajas :
    la portabilidad multiplataforma es relativamente buena y sigue los estándares POSIX.
2.modelo de encuesta:
(1) Proceso de operación:

① Defina la matriz de estructura de eventos descriptores monitoreados y agregue el descriptor y la información de identificación de eventos a monitorear a cada nodo de la matriz; ② Inicie una llamada para comenzar a monitorear y copie la matriz de estructuras de eventos descriptores al núcleo para su rotación
. A juicio, si está listo/esperando tiempo de espera, la llamada regresa y en cada estructura de evento listo, se representa el evento listo actual; ③
Lleve a cabo un sondeo y recorra la matriz para determinar en qué evento se encuentra el evento listo en cada nodo de la matriz. Determina si el descriptor está listo y qué hacer con él.

(2) Operación del código:
  •   int poll(struct pollfd* arry_fds,nfds_t nfds,int timeout)
      poll---监控采用时间结构体的形式;
      struct pollfd {
      	int fd ;---要监控的描述符;
      	short events; --- 要监控的事件POLLIN/POLLOUT;
      	short revents; --- 调用返回是填充的就绪事件
      	arry_fds---事件结构体数组,填充要监控的描述符以及事件信息;
      	nfds --- 数组中的有效节点数个数;
      	timeout---监控事件超时等待事件;
      	返回值:返回值大于0表示就绪描述符事件的个数;返回值等于0就表示等待超时,返回值小于0表示监控出错。
    
  #include <poll.h>
  #include <unistd.h>
  #include <stdio.h>
  int main() {
    
    
    struct pollfd poll_fd;//定义事件结构体 
    poll_fd.fd = 0;
    poll_fd.events = POLLIN;//输入事件                                                                                    
    for (;;) {
    
    
      int ret = poll(&poll_fd, 1, 1000);
      if (ret < 0) {
    
    
        perror("poll");
	       continue; 
      }
      if (ret == 0) {
    
    
        printf("poll timeout\n");
        continue;
      }
      if (poll_fd.revents == POLLIN) {
    
    
        char buf[1024] = {
    
    0};
        read(0, buf, sizeof(buf) - 1);
        printf("stdin:%s", buf);
      }
    }
  }
(3) Análisis de ventajas y desventajas:
  • Ventajas :
    ① El uso de estructuras de eventos para monitorear simplifica el proceso de operación de los tres eventos en selección.
    ② No existe un límite máximo en la cantidad de descriptores monitoreados.
    ③No es necesario redefinir los nodos de eventos cada vez.
  • Desventajas :
    ① Mala portabilidad multiplataforma.
    ②Cada monitoreo aún requiere copiar los datos de monitoreo al kernel.
    ③ La supervisión en el kernel todavía utiliza el recorrido de sondeo y el rendimiento disminuirá a medida que aumente el número de descriptores.
3.modelo eppol:
  • El modelo de multiplexación más útil y de mejor rendimiento en Linux.
(1) Proceso de operación:

① Inicie una llamada para crear la estructura epoll handle epollevent en el kernel (esta estructura contiene mucha información, árbol rojo-negro + lista enlazada bidireccional); ② Inicie una llamada para agregar/eliminar/modificar la información de monitoreo del descriptor
monitoreado a la estructura epollevent en el kernel;
③Inicie el inicio del monitoreo, use operaciones de bloqueo asincrónicas en el kernel para implementar el monitoreo, espere el tiempo de espera/cuando un descriptor esté listo, regresará y devolverá la información de la estructura del evento del descriptor listo del usuario ; ④El proceso responde directamente a la descripción en la
estructura del evento listo. Simplemente use los miembros del símbolo para realizar operaciones.

(2) Información de la interfaz:

① Cree un evento de control de epoll en el kernel y devuelva el descriptor

  •   int epoll _create(int size); :返回epoll操作句柄
      	-size : 在linux2.6.2以后被忽略,只要大于0即可。
      	返回值:文件描述符--epoll的操作句柄
    

②Función de registro de hora de epoll:

  •   int epoll_ctl(int epfd,int cmd, int fd, struct epoll_event* ev);
       -epfd : epoll_creat 返回的操作句柄;
       -cmd : 针对fd描述符的监控信息要进行的操作--添加/删除/修改 EPOLL_CTL_ADD/EPOLL_CTL_DEL/EPOLL_CTL_MOD;
       -fd : 要监控操作的描述符;
       -ev : fd描述符对应的事件结构体信息;
      	struct epoll_event{
      		uint32_t events; //对fd描述符要监控的事件--EPOLLIN/EPOLLOUT;
      		union{
      			int fd; // 监控操作的描述符;
      			void* ptr;//要填充的描述符信息;
      		}
      	}
    

③Recopilar eventos que se han enviado en el evento de monitoreo de epoll:

  •   int epoll_wait(int epfd,struct epoll_event *evs,int max_event,int timeout)
      	-epfd : epoll操作句柄;
      	-evs : struct epoll_event 结构体的首地址;
      	-max_event : 本次监控想要获取的就绪事件的最大数量,不大于evens数组的最大节点数量,禁止越界访问。
      	-timeout : 等待超时时间--单位:毫秒。
      	返回值:返回值<0 表示监控出错 ,返回值 == 0 表示超时返回 , 返回值 > 0 表示就绪的时间的个数。
    
(3) Principio de monitoreo de epoll: operación de bloqueo asíncrona:
  • El sistema completa el monitoreo. El descriptor de monitoreo y la estructura de eventos correspondiente agregada por el usuario se agregarán al árbol rojo-negro en la estructura de eventpoll del kernel. Una vez que se inicia una llamada para comenzar el monitoreo, el sistema operativo realiza una devolución de llamada. para cada evento de descriptor Función, la función es agregar la estructura a la lista doblemente enlazada cuando el descriptor esté listo para el evento de interés.
  • El proceso en sí solo determina si la lista doblemente enlazada es NULL cada vez y determina si está lista.
    ① Cree un identificador
    ② Agregue la información del descriptor monitoreado y la información de la estructura del evento correspondiente al kernel;
    ③ Inicie el monitoreo de bloqueo asíncrono y el sistema agrega la información de la estructura del evento correspondiente al descriptor listo a la lista doblemente enlazada;
    ④ Al juzgar la lista doblemente enlazada La estructura del evento se devuelve al proceso.
    ⑤ El proceso solo necesita determinar la operación correspondiente en el descriptor enviado en la estructura de tiempo en función de la información de tiempo en la estructura de evento listo.
(4) Ventajas y desventajas:
  • Ventajas :
    ① No hay límite superior en la cantidad de monitoreo sin descriptores;
    ② La información de monitoreo solo necesita agregarse al kernel una vez;
    ③ El monitoreo utiliza operaciones de bloqueo asincrónicas y el rendimiento no disminuirá con el aumento de operadores;
    ④ Directamente devuelve el tiempo de preparación a la información del usuario, el proceso opera directamente en el descriptor y evento devuelto, sin juzgar si está listo.
  • Desventajas :
    Mala portabilidad multiplataforma.
(5) Método de activación en epoll:
  • Supongamos que existe un ejemplo de este tipo :
    hemos agregado un socket tcp al descriptor de epoll.
    En este momento, se escriben 2 KB de datos en el otro extremo del socket.
    Llame a epoll_wait y regresará. Significa que está listo para la operación de lectura
    y luego llama a leer. Solo se leyó 1 KB de datos
    y continuó llamando a epoll_wait

①Método de activación horizontal:

  • El estado predeterminado de epoll es el modo de trabajo LT :
    cuando epoll detecta que el evento en el socket está listo, no necesita procesarlo inmediatamente, o solo procesa parte de él;
    como en el ejemplo anterior, ya que solo se han procesado 1K de datos. leído, todavía quedan 1K de datos en el búfer. Cuando se llama a epoll_wait por segunda vez, epoll_wait aún regresará inmediatamente y notificará al socket que el evento de lectura está listo; epoll_wait no regresará inmediatamente hasta que todos los datos en el
    búfer ha sido procesado,
    es decir, admite lectura y escritura con bloqueo y lectura sin bloqueo.

②Método de disparo por borde:

  • Si usamos el indicador EPOLLET al agregar el socket al descriptor de epoll en el paso 1, epoll ingresa al modo de trabajo ET: cuando
    epoll detecta que el evento en el socket está listo, debe procesarse inmediatamente;
    como en el ejemplo anterior, aunque solo se han leído 1K. Quedan 1K de datos en el buffer. Cuando se llama a epoll_wait por segunda vez, epoll_wait no regresará, es decir,
    en modo ET, después de que el evento en el descriptor de archivo esté listo, solo hay una oportunidad de procesamiento;
    ET El rendimiento es mayor que el de LT (epoll_wait devuelve muchas menos veces). Nginx usa el modo ET para usar epoll de forma predeterminada, es decir:
    solo admite lectura y escritura sin bloqueo.
  • Seleccionar y sondear realmente funcionan en modo LT. epoll puede admitir tanto LT como ET.

Supongo que te gusta

Origin blog.csdn.net/weixin_42357849/article/details/107785775
Recomendado
Clasificación