Principio de Redis: explicación detallada de IO

La dirección del texto original se actualiza y el efecto de lectura es mejor.

Principio de Redis: explicación detallada de IO | Mástil de programación de CoderMast icono-predeterminado.png?t=N5K3https://www.codermast.com/database/redis/redis-IO.html

Espacio de usuario y espacio del kernel

La versión de distribución de cualquier sistema Linux, su kernel de sistema es Linux. Todas nuestras aplicaciones necesitan interactuar con el hardware a través del kernel de Linux.

 

Para evitar que las aplicaciones de los usuarios causen conflictos o incluso pánicos del kernel, las aplicaciones de los usuarios se separan del kernel:

  • El espacio de direccionamiento de la memoria se divide en dos partes: el espacio del núcleo y el espacio del usuario.

Para un sistema operativo de 32 bits, la dirección de direccionamiento es 0 ~ 2322 ^ {32}232

  • Solo las instrucciones restringidas (Ring3) se pueden ejecutar en el espacio del usuario, y los recursos del sistema no se pueden llamar directamente y se debe acceder a ellos a través de la interfaz proporcionada por el kernel.
  • El espacio del kernel puede ejecutar comandos privilegiados (Ring0) y llamar a todos los recursos del sistema

Cuando un proceso se ejecuta en el espacio del usuario, se denomina modo de usuario, y cuando se ejecuta en el espacio del núcleo, se denomina modo del núcleo.

Para mejorar la eficiencia de IO, el sistema Linux agregará búferes tanto en el espacio del usuario como en el espacio del kernel:

  • Al escribir datos, los datos del búfer del usuario deben copiarse en el búfer del kernel y luego escribirse en el dispositivo
  • Leer datos es leer datos del dispositivo al búfer del kernel y luego copiarlos al búfer del usuario

 

5 modelos E/S

  1. E/S de bloqueo (E/S de bloqueo)
  2. E/S sin bloqueo (E/S sin bloqueo)
  3. Multiplexación IO (Multiplexación IO)
  4. E/S controlada por señal (E/S controlada por señal)
  5. IO asíncrono (IO asíncrono)

#bloqueo de E /S

Como su nombre lo indica, bloquear IO significa que debe bloquear y esperar durante las dos etapas de espera de datos y copia de datos en el espacio del usuario .

 

  1. Los subprocesos de usuario emiten solicitudes de IO
  2. El kernel verificará si los datos están listos, si no, esperará para siempre, y el subproceso del usuario estará en un estado bloqueado, y el subproceso del usuario estará en un estado bloqueado
  3. Cuando los datos estén listos, el kernel copiará los datos en el subproceso del usuario y devolverá el resultado al subproceso del usuario, y el subproceso del usuario desbloqueará el estado.

Se puede ver que en el modelo de IO de bloqueo, el proceso de usuario se bloquea en dos etapas.

# E/S sin bloqueo

La operación recvfrom de E/S sin bloqueo devuelve el resultado inmediatamente en lugar de bloquear el proceso del usuario.

 

  1. Esperando la etapa de datos, si los datos no están listos, devuelva EWOULDBLOCK inmediatamente. En este proceso, el proceso de usuario no bloquea, pero el proceso de usuario siempre iniciará solicitudes y estará ocupado con el entrenamiento de rotación hasta que el procesamiento del kernel comience a detener el entrenamiento de rotación.
  2. Una vez que los datos están listos, se copian del kernel al espacio del usuario. El proceso de usuario está bloqueado durante esta fase.

Se puede ver que en el modelo de E/S sin bloqueo, el proceso de usuario no bloquea en la primera fase y está bloqueado en la segunda fase. Aunque no bloquea, el rendimiento no ha mejorado y el mecanismo de espera ocupado hará que la CPU esté inactiva y el uso de la CPU aumentará considerablemente.

# Multiplexación de E/S

Ya sea que se trate de E/S de bloqueo o de E/S sin bloqueo, la aplicación de usuario debe llamar a recvfrom para obtener datos en la primera etapa. La diferencia radica en el método de procesamiento cuando no hay datos:

  • Si no hay datos cuando se llama a recvfrom, el bloqueo de E/S hará que el proceso se bloquee, y el no bloqueo de E/S hará que la CPU quede inactiva, lo que no puede desempeñar el papel de la CPU.
  • Si hay datos cuando se llama a recvfrom, el proceso de usuario puede ingresar directamente a la segunda etapa para leer y procesar los datos

Por ejemplo, cuando el servidor procesa las solicitudes de socket del cliente, en el caso de un solo subproceso, solo puede procesar cada socket a su vez. Si el socket que se procesa no está listo (los datos no se pueden leer ni escribir), el subproceso se bloqueará y todos los demás sockets del cliente deben esperar, y el rendimiento es naturalmente bajo.

Descriptor de archivo (File Descriptor): denominado FD, es un número entero sin signo que se incrementa desde 0, que se utiliza para asociar un archivo en Linux. Todo en Linux es un archivo, como archivos regulares, videos, dispositivos de hardware, etc., por supuesto, incluidos los sockets de red (Socket)

Multiplexación de IO: utiliza un único subproceso para monitorear múltiples FD al mismo tiempo, y se le notifica cuando un determinado FD se puede leer o escribir, para evitar esperas no válidas y aprovechar al máximo los recursos de la CPU.

 

Hay tres formas de implementar la tecnología de multiplexación de E/S:

  • seleccionar
  • encuesta
  • encuesta

diferencia:

  • seleccionar y sondear solo notificará al proceso de usuario que el FD está listo, pero no está seguro de qué FD es, y el proceso de usuario debe recorrer el FD uno por uno para confirmar
  • epoll notificará al proceso de usuario que el FD está listo y, al mismo tiempo, escribirá el FD listo en el espacio del usuario y ubicará directamente el FD listo.

# SELECCIONAR

select es la primera implementación de multiplexación de E/S en Linux:

// 定义类型别名 __fd_mask,本质是 long int
typedef long int __fd_mask;

/* fd_set 记录要监听的fd集合,及其对应状态 */
typedef struct {
    // fds_bits是long类型数组,长度为 1024/32 = 32
    // 共1024个bit位,每个bit位代表一个fd,0代表未就绪,1代表就绪
    __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
    // ...
} fd_set;


// select函数,用于监听多个fd的集合
int select(
    int nfds,// 要监视的fd_set的最大fd + 1
    fd_set *readfds,// 要监听读事件的fd集合
    fd_set *writefds,// 要监听写事件的fd集合
    fd_set *exceptfds,  // 要监听异常事件的fd集合
    // 超时时间,nulT-永不超时;0-不阻塞等待;大于0-固定等待时间
    struct timeval *timeout
);

 

El proceso específico es el siguiente:

  1. Crear fd_set rfds en el espacio del usuario
  2. Si desea monitorear fd = 1, 2, 5
  3. Ejecute selec(5 + 1, rfds, null, null, 3) en el espacio de usuario
  4. Copie la matriz fd_set rfds creada en el espacio del usuario al espacio del kernel
  5. Recorriendo la matriz fd_set rfds copiada en el espacio del kernel
  6. Si no está listo, establezca el fd en esa ubicación en 0.

Problemas con el modo de selección:

  • Es necesario copiar todo el fd_set del espacio del usuario al espacio del núcleo y volver a copiarlo al espacio del usuario después de seleccionar
  • Select no puede saber qué fd está listo, necesita atravesar fd_set
  • El número de fds monitoreados por fd_set no puede exceder 1024,

# ENCUESTA

El modo de encuesta ha realizado una mejora simple en el modo de selección, pero la mejora del rendimiento no es obvia. Algunos códigos clave son los siguientes:

// pollfd 中的事件类型
#define POLLIN      //可读事件
#define POLLOUT     //可写事件
#define POLLERR     //错误事件
#define POLLNVAL    //fd未打开

// pollfd结构
struct pollfd{
    int fd;             // 要监听的 fd
    *short int events;  // 要监听的事件类型:读、写、异常
    short int revents;  // 实际发生的事件类型
}

// poll函数
int poll(
    struct pollfd xfds, // pollfd数组,可以自定义大小
    nfds_t nfds,        // 数组元素个数
    int timeout         // 超时时间
);

Proceso de E/S:

  1. Cree una matriz pollfd, agregue la información fd correspondiente y personalice el tamaño de la matriz
  2. Llame a la función de encuesta, copie la matriz pollfd en el espacio del kernel, transfiérala a la lista vinculada para almacenamiento, sin límite superior
  3. El kernel atraviesa fd para determinar si está listo
  4. Después de que los datos estén listos o se agote el tiempo de espera, copie la matriz pollfd en el espacio del usuario y devuelva el número fd listo n
  5. El proceso de usuario juzga si n es mayor que 0
  6. Si es mayor que 0, recorra la matriz pollfd y encuentre el fd listo

Comparar con SELECCIONAR:

  • El valor fijo de fd_set en el modo de selección es 1024, mientras que pollfd usa una lista enlazada en el núcleo, que teóricamente es infinita
  • Cuantos más FD escuche, más tiempo llevará cada recorrido y, en cambio, el rendimiento se degradará.

#EPOLL _

El modo epoll es una mejora de los modos de selección y sondeo y proporciona tres funciones:

struct eventpoll{
    //...
    struct rb_root rbr; // 一颗红黑树,记录要监听的fd
    struct list_head rdlist;  // 一个链表,记录就绪的 FD
    //...
}

// 1.会在内核创建eventpolL结构体,返回对应的句柄epfd
int epoll create(int size);

// 2.将一个FD添加到epol的红黑树中,并设置ep_poli_calLback
// calTback触发时,就把对应的FD加入到rdlist这个就绪列表中
int epoll _ctl(
    int epfd,   // epoll实例的句柄
    int op,     // 要执行的操作,包括:ADD、MOD、DEL
    int fd,     // 要监听的 FD
    struct epoll_event *event // 要监听的事件类型: 读、写、异常等
);

// 3.检查rdlist列表是否为空,不为空则返回就绪的FD的数量
int epoll wait(
    int epfd,       // eventpoll 实例的句柄
    struct epoll_event *events, // 空event 数组,用于接收就绪的 FD
    int maxevents,  // events 数组的最大长度
    int timeout // 超时时间,-1永不超时;0不阻塞;大于0为阻塞时间
);

 

#mecanismo de notificación de eventos

Cuando FD tiene datos para leer, podemos llamar a epoll_wait para recibir una notificación, pero hay dos modos de notificación de tiempo:

  • Activado por nivel: LT para abreviar. Cuando FD tiene datos para leer, repetirá la notificación varias veces hasta que se complete el procesamiento de datos. Es el modo predeterminado de epoll.
  • Edge Triggered: ET para abreviar. Cuando FD tiene datos para leer, solo se le notificará una vez, independientemente de si los datos se han procesado o no.

Por ejemplo

  1. Suponga que el FD correspondiente a un cliente Socket se ha registrado en la instancia de epoll
  2. Client Socket envió 2kb de datos
  3. El servidor llama a epoll_wait y se le notifica que el FD está listo
  4. El servidor lee 1kb de datos de FD
  5. Vuelva al paso 3 (llame a epoll_wait nuevamente para formar un bucle)

en conclusión

  • El modo ET evita el fenómeno de grupo impactante que puede ocurrir en el modo LT
  • El modo ET se combina mejor con IO sin bloqueo para leer datos FD, que es más complicado que LT

# proceso de servicio WEB

Diagrama de flujo básico del servicio web basado en el modo epoll: 

#Resumen _

Hay tres problemas con el modo de selección:

  • El número máximo de FD que se pueden monitorear no excede 1024
  • Cada vez que seleccione, debe copiar todos los FD que se monitorearán en el espacio del kernel
  • Cada vez que sea necesario recorrer todos los FD para determinar el estado listo

Problemas con el modo de encuesta:

  • poll usa la lista enlazada para resolver el problema de monitorear el límite superior de FD en select, pero aún necesita atravesar todos los FD. Si hay más monitores, el rendimiento disminuirá

Cómo resolver estos problemas en modo epoll:

  • Basado en el árbol rojo-negro en la instancia de epoll para guardar los FD a monitorear, no hay un límite superior en teoría, y la eficiencia de agregar, eliminar, modificar y verificar es muy alta, y el rendimiento no disminuirá significativamente a medida que aumenta la cantidad de FD monitoreados
  • Cada FD solo necesita ejecutar epoll_ctl una vez para agregar al árbol rojo-negro, y luego cada epoll_wait no necesita pasar ningún parámetro, y no hay necesidad de copiar repetidamente FD al espacio del kernel
  • El kernel copiará directamente el FD listo en la ubicación especificada en el espacio del usuario, y el proceso de usuario puede saber quién es el FD listo sin atravesar todos los FD.

# E/ S impulsada por señal

El IO controlado por señal es para establecer una asociación de señal SIGIO con el kernel y configurar una devolución de llamada. Cuando el kernel tiene un FD listo, enviará una señal SIGIO para notificar al usuario. Durante este período, la aplicación del usuario puede realizar otros servicios sin bloquearse ni esperar.

 

Cuando hay una gran cantidad de operaciones IO, hay muchas señales y la función de procesamiento SIGIO no puede manejarlas a tiempo, lo que puede causar que la cola de señales se desborde.

Además, el rendimiento de la interacción frecuente de señales entre el espacio del núcleo y el espacio del usuario también es bajo.

#E/ S asíncrona

Todo el proceso de E/S asíncrona es sin bloqueo. Después de que el proceso de usuario llama a la API asíncrona, puede hacer otras cosas. El kernel espera a que los datos estén listos y los copia en el espacio del usuario antes de enviar la señal para notificar al proceso de usuario.

 

En el modelo de E/S asíncrono, el proceso de usuario no bloquea en ambas fases.

Aunque el modelo de E/S asíncrono es muy simple, bajo un alto acceso simultáneo, se procesará una gran cantidad de solicitudes en el kernel, lo que puede conducir fácilmente a un bloqueo del kernel.

# síncrono y asíncrono

Que la operación de IO sea síncrona o asíncrona depende del proceso de copia de datos entre el espacio del kernel y el espacio del usuario (operación de IO de lectura y escritura de datos), es decir, si la segunda fase es síncrona o asíncrona:

Supongo que te gusta

Origin blog.csdn.net/qq_33685334/article/details/131324401
Recomendado
Clasificación