Red de conocimiento informático - Consumibles informáticos - Bloqueo, no bloqueo, multiplexación, síncrono, asíncrono, BIO, NIO, AIO se tratan en un solo artículo.

Bloqueo, no bloqueo, multiplexación, síncrono, asíncrono, BIO, NIO, AIO se tratan en un solo artículo.

Con respecto a IO, implicará varios puntos de conocimiento como bloqueo, no bloqueo, multiplexación, sincronización, asíncrono, BIO, NIO, AIO, etc. Aunque los puntos de conocimiento no son difíciles, a menudo se confunden fácilmente. Me gustaría marcar esto para animarlos.

En el caso de bloquear IO, cuando el usuario llama a leer, el hilo del usuario se bloqueará y la lectura no regresará hasta que los datos del kernel estén listos y los datos se copien del búfer del kernel al estado del usuario. buffer. Puedes ver las dos partes del bloqueo.

Después de que IO sin bloqueo emite una solicitud de lectura y descubre que los datos no están listos, continuará ejecutándose. En este momento, la aplicación sondeará continuamente el núcleo de sondeo para preguntar si los datos están listos. Cuando los datos no están listos, el kernel devuelve el error EWOULDBLOCK inmediatamente. La solicitud de lectura no obtiene el resultado hasta que los datos se copian al búfer de la aplicación. ¡Y hay que prestar atención! El proceso de obtención de datos durante la última llamada de lectura aquí es un proceso sincrónico y un proceso que requiere espera. La sincronización aquí se refiere al proceso de copiar datos del estado del kernel al área de caché del programa de usuario.

Cuando no hay datos disponibles en condiciones sin bloqueo, la aplicación sondea el kernel cada vez para ver si los datos están listos, lo que también consume la CPU. ¿Puedes evitar que sondee el kernel? Los datos del búfer están listos. ¿Cómo utilizar el mecanismo de notificación de eventos para informar al proceso de aplicación que los datos están listos? El proceso de solicitud puede estar ocupado escribiendo otros trabajos cuando no recibe la señal de notificación del evento de datos listos. En este punto, la multiplexación IO resulta útil.

La multiplexación de IO en chino es bastante confusa. El texto original de la multiplexación de IO se llama multiplexación de E/S. La multiplexación aquí en realidad se refiere al seguimiento de cada estado de Sock (flujo de E/S) para gestionar múltiples E/S. transmite simultáneamente. El propósito de su invención es mejorar la capacidad de rendimiento del servidor tanto como sea posible. Implemente un hilo para monitorear múltiples solicitudes de IO. Si hay una solicitud de IO, los datos se copiarán desde el kernel al búfer del proceso. ¡El período de copia se bloqueará! Ahora es posible lograr el mejor efecto de compartir memoria utilizando el método de asignación de direcciones mmap, evitar la copia verdadera y mejorar la eficiencia.

Cosas como select, poll y epoll son implementaciones específicas de multiplexación de E/S.

Select es la primera versión de reutilización de IO y muchos problemas quedaron expuestos después de su propuesta.

La encuesta soluciona muchos problemas con select.

Pero la encuesta aún no es segura para subprocesos, lo que significa que no importa cuán poderoso sea el servidor, solo puede procesar un conjunto de flujos de E/S en un subproceso. Por supuesto, puede utilizar múltiples procesos para cooperar, pero luego tendrá varios problemas con múltiples procesos.

Se puede decir que epoll es la última implementación de multiplexación de E/S. epoll ha solucionado la mayoría de los problemas de sondeo y selección, como:

El eje horizontal Las conexiones muertas son. el número de enlaces. Es decir, el nombre es solo que su herramienta de prueba se llama deadcon. El eje vertical es la cantidad de solicitudes procesadas por segundo. Se puede ver que la cantidad de solicitudes procesadas por epoll por segundo básicamente no disminuirá a medida que aumente la cantidad de enlaces. poll y /dev/poll son miserables. Pero epoll tiene la desventaja fatal de que sólo es compatible con Linux.

Por ejemplo, la razón por la que Nginx puede admitir QPS de 4W es porque utiliza el modelo de multiplexación de E/S más eficiente en la plataforma de destino.

Entonces encontrará que ninguna de las operaciones mencionadas anteriormente es verdaderamente asincrónica, ¡porque las dos etapas siempre tienen que esperar un tiempo! La verdadera E/S asincrónica significa que no es necesario esperar a que se preparen los datos del kernel y se copien los datos del estado del kernel al estado de usuario.

Afortunadamente, Linux ha preparado las funciones aio_read y aio_write para que podamos lograr una verdadera operación asincrónica. Cuando el usuario inicia una solicitud aio_read, regresará automáticamente. El kernel copiará automáticamente los datos del búfer del kernel al espacio de proceso del usuario y el proceso de la aplicación no necesita preocuparse por nada.

Recomiendo encarecidamente la dirección de aprendizaje gratuita para el desarrollo back-end de C: desarrollo de servidor C/C Linux/arquitecto backend Lingsheng Education-Learning Video Tutorial-Tencent Classroom

La diferencia entre sincrónico y asincrónico Depende de si el subproceso del usuario completa la copia de datos del espacio del kernel al espacio del usuario. Se divide en dos tipos: bloqueo sincrónico y sin bloqueo sincrónico.

Tomemos como ejemplo el no bloqueo sincrónico. Como puede ver a continuación, el proceso de copia de datos del kernel al espacio del usuario se completa mediante el bloqueo del hilo del usuario.

Se puede encontrar que el usuario regresará inmediatamente después de la llamada, y el kernel completará la copia de los datos y notificará al hilo del usuario para que realice una devolución de llamada.

En Java, utilizamos sockets para la comunicación de red. Hay tres modos IO principales, que dependen principalmente de lo que admita el kernel.

IO de bloqueo sincrónico. Para cada solicitud de conexión de Socket del cliente, el servidor tendrá un hilo de procesamiento correspondiente. Las conexiones que no estén asignadas a un hilo de procesamiento serán bloqueadas o rechazadas. Equivale a una conexión y un hilo.

Funciones BIO:

Constantes:

Clase principal:

Hilo de escucha del lado del servidor:

Servidor -Subproceso de procesamiento del lado:

Cliente:

IO NIO síncrono sin bloqueo: el servidor guarda una lista de conexiones de Socket y luego sondea esta lista si encuentra un puerto de Socket. son datos legibles, significa que la lectura está lista y se llama a la operación de lectura correspondiente de la conexión de socket. Si se descubre que hay datos para escribir en un puerto de socket, significa que la escritura está lista y se llama a la operación de escritura correspondiente de la conexión de socket. Si la conexión de Socket de un determinado puerto se ha interrumpido, llame al método destructor correspondiente para cerrar el puerto. Esto puede hacer un uso completo de los recursos del servidor y la eficiencia se ha mejorado enormemente. Al realizar solicitudes de operación IO, se utiliza un subproceso para procesarlo, que es "un subproceso por solicitud". Selector, Canal y Buffer se utilizan en Java para lograr los efectos anteriores.

Cada hilo contiene un objeto Selector, que es equivalente a un administrador de canales, que puede procesar múltiples canales en un hilo y reducir la cantidad de hilos creados. La conexión remota corresponde a un canal, y la lectura y escritura de datos se completan en el mismo canal a través del búfer, y la lectura y escritura de datos no son bloqueantes. Una vez creado el canal, debe registrarse en el selector. Al mismo tiempo, es necesario registrar eventos interesantes para el canal (evento de conexión del cliente, evento de conexión del cliente del servidor, evento de lectura, evento de escritura). El hilo del selector necesita usar el sondeo para llamar al selector. La función de selección regresará hasta que ocurran los eventos de interés en todos los canales registrados; de lo contrario, se bloqueará. Luego recorre todos los eventos de interés ya preparados.

Los pasos anteriores resuelven los dos cuellos de botella de BIO:

La siguiente es una breve introducción a los siguientes tres conceptos. Java NIO consta de las siguientes tres partes principales:

El canal y el búfer son. bueno Varios tipos. Estas son las implementaciones de algunos de los canales principales en Java NIO:

Como puede ver, estos canales cubren IO de red UDP y TCP, así como IO de archivos. Las siguientes son las implementaciones de búfer clave en Java NIO:

En la etapa de microservicios, una solicitud puede implicar llamadas entre servidores entre múltiples servicios diferentes si desea implementar un marco PRC de alto rendimiento para el procesamiento de datos. Para la transmisión, puede crear un marco basado en Java NIO que admita conexiones largas, protocolos personalizados y alta concurrencia, como Netty. Netty en sí es un marco de red basado en NIO, que encapsula los complejos detalles subyacentes de Java NIO y le proporciona conceptos abstractos de programación simples y fáciles de usar. Por ejemplo, la capa inferior de Dubbo usa Netty.

AIO es un IO asincrónico sin bloqueo, que es un paso más allá que NIO. Cuando el proceso lee datos, solo es responsable de enviar y recibir instrucciones. La preparación de los datos está completamente a cargo del sistema operativo. .

Recomiende un curso público gratuito sobre desarrollo de backend de C/C impartido por Zero Sound Education. Personalmente, creo que el profesor lo enseñó bien. Me gustaría compartirlo con usted: arquitecto senior de desarrollo de backend de C/C, incluido. Linux, Nginx, ZeroMQ, MySQL, Redis, fastdfs, MongoDB, ZK, streaming media, CDN, P2P, K8S, Docker, TCP/IP, coroutine, DPDK y otro contenido técnico, desarrollo de servidor C/C Linux/arquitecto backend Zero Voice Videotutorial de educación y aprendizaje: Tencent Classroom Aprenda ahora

Texto original: bloqueo, no bloqueo, multiplexación, sincrónico, asincrónico, BIO, NIO, AIO, todo en un solo recipiente