Cómo mejorar el rendimiento general del dispositivo de bloque IO en Linux
Prólogo: este artículo explica principalmente los tres modos de la capa de programación IO de Linux: cfp, fecha límite y noop, y ofrece sugerencias de optimización y escenarios aplicables para cada uno.
La programación de IO ocurre en la capa de programación de IO del kernel de Linux. Este nivel es para la jerarquía IO general de Linux. Desde la perspectiva de las llamadas al sistema read() o write(), el sistema IO general de Linux se puede dividir en siete capas, que son:
Capa VFS: capa del sistema de archivos virtual. Dado que el kernel tiene que lidiar con múltiples sistemas de archivos y las estructuras de datos y los métodos relacionados implementados por cada sistema de archivos pueden ser diferentes, el kernel abstrae esta capa específicamente para adaptarse a varios sistemas de archivos y proporciona una interfaz de operación unificada al mundo exterior. .
Capa del sistema de archivos: los diferentes sistemas de archivos implementan sus propios procesos operativos y proporcionan sus propias características únicas. Si lo desea, puede consultar el código usted mismo.
Capa de almacenamiento en caché de la página: Responsable del almacenamiento en caché de la página.
Capa de bloque general: dado que la mayoría de las operaciones de io tratan con dispositivos de bloque, Linux proporciona una capa de abstracción de operaciones de dispositivos de bloque similar a la capa vfs. La capa inferior se conecta a varios dispositivos de bloque con diferentes atributos y proporciona un estándar de solicitud de Block IO unificado para la capa superior.
Capa de programación IO: debido a que la mayoría de los dispositivos de bloque son dispositivos similares a discos, es necesario configurar algunos algoritmos de programación y colas diferentes según las características de dichos dispositivos y las diferentes características de las aplicaciones. Para mejorar la eficiencia de lectura y escritura del disco de manera específica en diferentes entornos de aplicaciones, aquí es donde entra en juego el famoso ascensor de Linux. Aquí se implementan varios métodos de programación para discos duros mecánicos.
Capa de controlador de dispositivo de bloque: la capa de controlador proporciona interfaces de operación de dispositivo de nivel relativamente alto para el mundo exterior, a menudo en lenguaje C, mientras que la capa inferior interactúa con los métodos operativos y las especificaciones del dispositivo en sí.
Capa de dispositivo de bloque: esta capa es el dispositivo físico específico y define varios métodos y especificaciones de operación del dispositivo real.
Hay un [diagrama de estructura de Linux IO] compilado, que es muy clásico. Una imagen vale más que mil palabras:
El contenido que vamos a estudiar hoy se centra principalmente en el. Capa de programación IO.
El problema principal que quiere resolver es: ¿cómo mejorar el rendimiento general del dispositivo de bloque IO? Esta capa también está diseñada principalmente para la estructura mecánica del disco duro.
Como todos sabemos, el medio de almacenamiento de un disco duro mecánico es un disco magnético. El cabezal magnético se mueve sobre el disco para realizar el direccionamiento de pistas, lo que se comporta como si estuviera reproduciendo un disco.
La característica de esta estructura es que el rendimiento es alto durante el acceso secuencial. Sin embargo, si hay acceso aleatorio al disco, se perderá mucho tiempo en el movimiento del cabezal magnético. conduce a cada El tiempo de respuesta de IO secundario se vuelve más largo, lo que reduce en gran medida la velocidad de respuesta de IO.
La operación de búsqueda del cabezal magnético en el disco es similar a la programación del ascensor. De hecho, al principio, Linux llamó a este algoritmo algoritmo del ascensor de Linux, es decir:
. Si está buscando En el proceso de seguimiento, las solicitudes de datos de las pistas relacionadas que pasan en secuencia se pueden procesar "por cierto", por lo que el rendimiento general de IO se puede mejorar con un impacto relativamente pequeño en la velocidad de respuesta.
Es por eso que diseñamos el algoritmo de programación IO.
Actualmente, tres algoritmos/modos están habilitados de forma predeterminada en el kernel: noop, cfq y fecha límite. Estrictamente hablando, debería haber dos tipos:
Porque el primero se llama noop, que es un algoritmo de programación sin operación, es decir, no hay operación de programación y no ordena solicitudes de io. pero solo se fusiona una quincena de colas de io apropiadas.
El algoritmo de programación predeterminado actual en el kernel debería ser cfq, que se denomina programación de cola completamente justa. Este algoritmo de programación hace honor a su nombre e intenta proporcionar un entorno operativo IO completamente justo para todos los procesos.
Nota: recuerde esta palabra, cfq, programación de colas completamente justa; de lo contrario, no podrá leer lo siguiente.
cfq crea una cola de programación de IO sincrónica para cada proceso y asigna recursos de IO en un intervalo de tiempo y número de solicitudes limitado de forma predeterminada para garantizar que la ocupación de recursos de IO de cada proceso sea justa. -Programación de prioridades a nivel, que explicaremos en detalle más adelante.
El método para ver y modificar el algoritmo de programación de IO es:
cfq es una mejor opción de algoritmo de programación de IO para servidores generales y una mejor opción para usuarios de escritorio.
Sin embargo, no es muy adecuado para muchos escenarios con alta presión IO, especialmente escenarios donde la presión IO se concentra en ciertos procesos.
Debido a este escenario, debemos satisfacer la velocidad de respuesta de IO de uno o algunos procesos, en lugar de permitir que todos los procesos usen IO de manera justa, como las aplicaciones de bases de datos.
La programación de fechas límite (programación de fechas límite) es una solución más adecuada para el escenario anterior. Deadline implementa cuatro colas:
Dos de ellas manejan lectura y escritura normales respectivamente, ordenan por número de sector y realizan fusiones de E/S normales para mejorar el rendimiento. Debido a que las solicitudes de IO pueden concentrarse en ciertas ubicaciones de disco, esto hará que se fusionen nuevas solicitudes todo el tiempo y las solicitudes de IO de otras ubicaciones de disco pueden morir de hambre.
Las otras dos colas que manejan el tiempo de espera de lectura y escritura se clasifican según el tiempo de creación de la solicitud. Si se produce una solicitud de tiempo de espera, se coloca en estas dos colas. El algoritmo de programación garantiza que se cumpla el tiempo de espera (la fecha límite). alcanzado) Las solicitudes en la cola se procesarán primero para evitar que las solicitudes mueran de hambre.
No hace mucho, el kernel estaba equipado con cuatro algoritmos como estándar por defecto, y también había un algoritmo llamado (Programador anticipatorio), un algoritmo de programación predictiva. Un nombre tan elevado me hizo pensar que el kernel de Linux podría adivinar la suerte.
Resulta que no es más que esperar un poco antes de realizar la programación de io según el algoritmo de fecha límite. Si hay solicitudes de io que se pueden fusionar durante este período, se pueden fusionar y procesar. para mejorar la eficiencia de la programación de plazos. Rendimiento de datos en situaciones de lectura y escritura secuenciales.
En realidad, esto no es una predicción en absoluto. Creo que es mejor llamarlo un algoritmo de programación afortunado. Por supuesto, esta estrategia funciona bien en algunos escenarios específicos.
Pero en la mayoría de los escenarios, esta programación no solo no mejora el rendimiento, sino que también reduce la velocidad de respuesta, por lo que el kernel simplemente la elimina de la configuración predeterminada. Después de todo, el propósito de Linux es ser práctico y no perderemos más tiempo con este algoritmo de programación.
1.cfq: programación de colas completamente justa
cfq es la cola de programación de IO seleccionada por el kernel de forma predeterminada. Es una buena opción en escenarios de aplicaciones de escritorio y en los escenarios de aplicaciones más comunes. .
¿Cómo implementar las llamadas colas completamente justas?
En primer lugar, debemos entender quién es justo con quién. Desde la perspectiva del sistema operativo, los sujetos que generan comportamientos operativos son procesos, por lo que la equidad aquí es para cada proceso. Debemos intentar permitir que el proceso ocupe los recursos de IO de manera justa.
Entonces, ¿cómo dejar que el proceso ocupe los recursos de IO de manera justa? Primero debemos comprender qué son los recursos de IO. Cuando medimos un recurso de IO, generalmente nos gusta usar dos unidades, una es el ancho de banda de lectura y escritura de datos y la otra es el IOPS de lectura y escritura de datos.
El ancho de banda es la cantidad de datos leídos y escritos en el tiempo, por ejemplo, 100 Mbyte/s. IOPS es la cantidad de lecturas y escrituras en el tiempo. En diferentes situaciones de lectura y escritura, el rendimiento de estas dos unidades puede ser diferente, pero lo cierto es que si cualquiera de las dos unidades alcanza el límite superior de rendimiento, se convertirá en un cuello de botella de IO.
Teniendo en cuenta la estructura del disco duro mecánico, si la lectura y la escritura son secuenciales, entonces el rendimiento de IO puede lograr un mayor ancho de banda con relativamente pocos IOPS, porque se pueden combinar muchas IO y también se puede realizar la lectura previa. realizados y otros métodos para acelerar la eficiencia de lectura de datos.
Cuando el rendimiento de IO está sesgado hacia la lectura y escritura aleatoria, las IOPS aumentarán y la posibilidad de fusionar solicitudes de IO disminuirá. Cuando los datos de cada solicitud de IO sean menores, el rendimiento del ancho de banda será menor.
Desde aquí podemos entender que hay dos manifestaciones principales de los recursos de IO para un proceso: la cantidad de solicitudes de IO enviadas por el proceso dentro de una unidad de tiempo y el ancho de banda de IO ocupado por el proceso.
De hecho, no importa cuál, está estrechamente relacionado con la duración del tiempo de procesamiento de IO asignado por el proceso.
A veces las empresas pueden ocupar un mayor ancho de banda con menos IOPS, mientras que otras pueden ocupar menos ancho de banda con un mayor IOPS, por lo que es relativo programar el tiempo que el proceso ocupa IO.
Es decir, no me importa si tienes un alto IOPS o un alto uso de ancho de banda. Cuando llegue el momento, cambiaremos el proceso al siguiente, como quieras.
Entonces, cfq intenta asignar intervalos de tiempo iguales para los dispositivos de bloque a todos los procesos. Dentro del intervalo de tiempo, el proceso puede enviar las solicitudes de IO generadas al dispositivo de bloque para su procesamiento. proceso La solicitud se pondrá en cola en su propia cola, esperando ser procesada la próxima vez que se programe. Este es el principio básico de cfq.
Por supuesto, no existe una verdadera "justicia" en la vida real. En escenarios de aplicación comunes, es posible que necesitemos especificar artificialmente prioridades para la ocupación IO del proceso, que es como priorizar la ocupación IO del proceso. proceso El concepto de uso de CPU es el mismo que establecer prioridad.
Por lo tanto, además de una programación justa de colas para intervalos de tiempo, cfq también proporciona soporte prioritario. Cada proceso puede establecer una prioridad de IO y cfq utilizará esta configuración de prioridad como un factor de referencia importante en la programación.
La prioridad se divide primero en tres categorías: RT, BE, IDLE, que son Tiempo real, Mejor intento e Inactivo respectivamente. Para cada categoría IO, cfq utiliza diferentes estrategias de procesamiento. Además, en las categorías RT y BE, 8 subprioridades se dividen para implementar requisitos de QOS más detallados, mientras que IDLE tiene solo una subprioridad.
Además, todos sabemos que el kernel lee y escribe el almacenamiento a través del caché (búfer/caché) de forma predeterminada. En este caso, cfq no puede distinguir de qué proceso proviene la solicitud actualmente procesada.
Solo cuando el proceso usa sincronización (lectura sincronizada o conexión sincronizada) o IO directa (Direct IO) para leer y escribir, cfq puede distinguir de qué proceso proviene la solicitud de IO.
Entonces, además de la cola IO implementada para cada proceso, también se implementa una cola pública para manejar solicitudes asincrónicas.
El kernel actual ha implementado el aislamiento de recursos de cgroup para los recursos de IO, por lo que, según el sistema anterior, cfq también implementa soporte de programación para cgroups.
En general, cfq utiliza una serie de estructuras de datos para admitir todas las funciones complejas anteriores. Puede ver su implementación relacionada a través del código fuente. El archivo está en block/cfq en el directorio de código fuente. iosched.c.
1.1 Principio de diseño de cfq
Aquí damos una breve descripción de la estructura de datos general: Primero, cfq mantiene todo el proceso del programador a través de una estructura de datos llamada cfq_data. En un cfq que admite la función cgroup, todos los procesos se dividen en varios grupos centrales para su gestión.
Cada cgroup tiene una estructura cfq_group en cfq para describirlo. Todos los cgroups se colocan en un árbol rojo-negro como un objeto de programación y se ordenan con vdisktime como clave.
El tiempo vdisktime registra el tiempo de IO ocupado por el cgroup actual. Cada vez que se programa un cgroup, el cgroup con el tiempo vdisktime actual más pequeño siempre se selecciona a través del árbol rojo-negro para su procesamiento para garantizar que todos. cgroups La ocupación de recursos de IO entre ellos es "justa".
Por supuesto, sabemos que los cgroups pueden asignar recursos a blkio proporcionalmente. El principio es que el tiempo de vdisktime ocupado por los cgroups con una proporción de asignación grande aumenta lentamente, y el tiempo de vdisktime ocupado por los cgroups con una proporción pequeña. El ratio de asignación crece más rápidamente. Directamente proporcional al ratio de distribución.
Esto garantiza que diferentes cgroups asignen diferentes proporciones de IO, y sigue siendo "justo" desde la perspectiva de cfq.
Después de seleccionar el cgroup (cfq_group) que debe procesarse, el programador debe decidir sobre el siguiente service_tree.
La estructura de datos service_tree corresponde a una serie de árboles rojo-negro. Su objetivo principal es implementar la clasificación de prioridad de solicitudes, que es la clasificación de RT, BE e IDLE. Cada cfq_group mantiene 7 service_trees, que se definen de la siguiente manera:
Service_tree_idle es el árbol rojo-negro utilizado para poner en cola las solicitudes de tipo IDLE.
En cuanto a la matriz bidimensional anterior, en primer lugar, la primera dimensión implementa una matriz para RT y BE respectivamente. Cada matriz mantiene tres árboles rojo-negro, correspondientes a tres subtipos diferentes. : SINCRONIZACIÓN, SYNC_NOIDLE y ASYNC.
Podemos pensar en SYNC como equivalente a SYNC_IDLE y correspondiente a SYNC_NOIDLE. El inactivo es un mecanismo agregado por cfq para fusionar solicitudes de IO continuas tanto como sea posible para mejorar el rendimiento. Podemos entenderlo como un mecanismo de espera "inactivo".
La rotación inactiva significa que cuando una cola termina de procesar una solicitud, esperará un breve período de tiempo antes de que se produzca la programación. Si llega la siguiente solicitud, el direccionamiento principal se puede reducir y la E/S secuencial puede continuar. procesado.
Para realizar esta función, cfq implementa la cola SYNC en la estructura de datos de service_tree. Si la solicitud es una solicitud de secuencia sincrónica, se pondrá en cola en el árbol de servicios. solicitud, se pondrá en cola en SYNC_NOIDLE Queue para determinar si la siguiente solicitud es una solicitud secuencial.
Todas las solicitudes de operaciones de escritura asincrónicas se pondrán en cola en el árbol de servicios ASYNC y no existe ningún mecanismo de espera inactivo para esta cola.
Además, cfq también tiene ajustes especiales para discos duros como SSD. Cuando cfq descubre que el dispositivo de almacenamiento es un dispositivo con una mayor profundidad de cola, como un disco duro SSD, todas las colas individuales estarán inactivas. no tendrá efecto. Todas las solicitudes de IO se pondrán en cola en el árbol de servicios SYNC_NOIDLE.
Cada árbol de servicios corresponde a varias colas cfq_queue, y cada cola cfq_queue corresponde a un proceso. Explicaremos esto en detalle más adelante.
cfq_group también mantiene una cola de solicitudes de IO asincrónicas común a todos los procesos dentro del cgroup. Su estructura es la siguiente:
Las solicitudes asincrónicas también se dividen en tres categorías: RT, BE y. IDLE para procesamiento, cada categoría corresponde a un cfq_queue para hacer cola.
BE y RT también implementan soporte de prioridad. Cada tipo tiene múltiples prioridades, como IOPRIO_BE_NR. Este valor se define como 8 y el subíndice de matriz es 0-7.
La versión del código del kernel que estamos analizando actualmente es Linux 4.4. Se puede ver que desde la perspectiva de cfq, el soporte de cgroup para IO asíncrono ya se puede implementar. Aquí necesitamos definir el significado de IO asíncrono. Solo representa la solicitud de IO que sincroniza los datos en el búfer/caché de la memoria con el disco duro, en lugar de aio (man 7 aio) o los mecanismos io y libaio asincrónicos nativos de Linux. "Los mecanismos de IO que están en el kernel se implementan de forma sincrónica (en esencia, las computadoras von Neumann no tienen un mecanismo "asincrónico" real).
Hemos explicado anteriormente que dado que el proceso normalmente escribe datos en el búfer/caché primero, este tipo de E/S asíncrona es manejado uniformemente por la cola de solicitudes asíncronas en cfq_group.
Entonces, ¿por qué necesitamos implementar un tipo ASYNC en el árbol de servicios anterior?
Esto, por supuesto, es para prepararse para la IO asincrónica que distingue los procesos y los hace "completamente justos".
De hecho, en el último sistema blkio de cgroup v2, el kernel ya admite el límite de velocidad de cgroup para IO del búfer, y los tipos anteriores que pueden confundirse fácilmente están todos bajo las etiquetas de tipo del nuevo sistema. utilizarse.
El nuevo sistema es más complejo y más potente, pero no se preocupe todavía. El sistema oficial cgroup v2 se le presentará oficialmente cuando se lance Linux 4.5.
Continuamos el proceso de selección de service_tree. La selección de tres tipos de prioridad de service_tree se basa en la prioridad del tipo. RT tiene la prioridad más alta, BE la segunda y IDLE la más baja. Es decir, si hay RT en él, RT se procesará todo el tiempo, y si RT desaparece, se procesará BE.
Cada service_tree corresponde a un árbol rojo-negro cuyos elementos están en cola para cfq_queue, y cada cfq_queue es una cola de solicitudes creada por el núcleo para un proceso (hilo).
Cada cfq_queue mantendrá una variable rb_key, que en realidad es el tiempo de servicio IO de la cola.
Aquí todavía usamos el árbol rojo-negro para encontrar el cfq_queue con el tiempo de servicio más corto para garantizar la "completa equidad".
Después de seleccionar cfq_queue, debe comenzar a procesar las solicitudes de IO en esta cola. El método de programación aquí es básicamente similar a la fecha límite.
cfq_queue pondrá en cola cada solicitud que ingrese a la cola dos veces, una en el quince y la otra en el árbol rojo-negro que se ingresa en el orden de los sectores accedidos.
De forma predeterminada, las solicitudes se toman del árbol rojo-negro para su procesamiento. Cuando el tiempo de demora de la solicitud llega a la fecha límite, el tiempo de espera más largo se toma del árbol rojo-negro para su procesamiento para garantizar que. la solicitud no morirá de hambre.
Este es todo el proceso de programación de cfq. Por supuesto, todavía hay muchos detalles que no se han explicado, como el procesamiento de fusión, el procesamiento secuencial, etc.
1.2 Ajuste de parámetros de cfq
Comprender todo el proceso de programación nos ayuda a decidir cómo ajustar los parámetros relacionados con cfq. Todos los parámetros ajustables de cfq se pueden encontrar en el directorio /sys/class/block/sda/queue/iosched/. Por supuesto, reemplace sda con el nombre del disco correspondiente en su sistema. Echemos un vistazo a lo que tienen:
Estos parámetros están relacionados en parte con el método de búsqueda de cabezal del disco duro mecánico. Si no comprende la explicación, agregue primero los conocimientos relevantes:
back_seek_max: El rango máximo que el cabezal magnético puede buscar hacia atrás. El valor predeterminado es 16M.
back_seek_penalty: Coeficiente de penalización por direccionamiento hacia atrás. Este valor se compara con el direccionamiento directo.
Los dos anteriores están configurados para evitar que el cabezal de búsqueda vibre y provoque que el direccionamiento sea demasiado lento. La idea básica es que cuando llega una solicitud de IO, cfq estimará el costo de búsqueda de cabeza en función de su posición de direccionamiento.
Establezca un valor máximo back_seek_max. Para solicitudes donde el número de sector al que se accede está detrás del encabezado, siempre que el rango de direccionamiento no exceda este valor, cfq lo procesará como una solicitud de direccionamiento directo.
Establezca un coeficiente back_seek_penalty para evaluar el costo. En comparación con el direccionamiento directo del cabezal magnético, cuando la distancia del direccionamiento inverso es 1/2 (1/back_seek_penalty), cfq considera que se abordan las dos solicitudes. El precio es el mismo.
Estos dos parámetros son en realidad restricciones condicionales para que cfq juzgue el procesamiento de fusión de solicitudes. Todas las solicitudes que combinen esta condición se fusionarán y procesarán juntas tanto como sea posible durante el procesamiento de esta solicitud.
fifo_expire_async: establece el tiempo de espera para solicitudes asincrónicas.
Las solicitudes sincrónicas y las solicitudes asincrónicas se procesan en diferentes colas al programar, cfq generalmente procesará las solicitudes sincrónicas primero y luego las solicitudes asincrónicas, a menos que las solicitudes asincrónicas cumplan con las condiciones anteriores para el procesamiento de fusión.
Cuando se programa la cola de este proceso, cfq dará prioridad a verificar si existe un tiempo de espera de solicitud asincrónica que exceda el límite del parámetro fifo_expire_async. Si lo hay, se enviará primero una solicitud con tiempo de espera agotado y las solicitudes restantes se seguirán procesando según la prioridad y el tamaño del número de sector.
fifo_expire_sync: este parámetro es similar al anterior, excepto que se utiliza para establecer el tiempo de espera de la solicitud de sincronización.
slice_idle: El parámetro establece un tiempo de espera. Esto permite que cfq espere un período de tiempo al cambiar de cfq_queue o árbol de servicios. El propósito es mejorar el rendimiento del disco duro mecánico.
Generalmente, las solicitudes de E/S del mismo cfq_queue o árbol de servicios tienen una mejor localidad de direccionamiento, por lo que esto puede reducir la cantidad de tiempos de direccionamiento del disco. Este valor predeterminado es distinto de cero en discos duros mecánicos.
Por supuesto, establecer este valor en un valor distinto de cero en una unidad de estado sólido o un dispositivo RAID duro reducirá la eficiencia del almacenamiento, porque las unidades de estado sólido no tienen el concepto de direccionamiento principal, por lo que en dichos dispositivos debe establecerse en 0 y desactivarse.
group_idle: este parámetro es similar al parámetro anterior, la diferencia es que cuando cfq quiere cambiar de cfq_group, esperará un período de tiempo.
En el escenario de cgroup, si seguimos el método slice_idle, puede ocurrir una espera inactiva cuando se cambia el cfq_queue de cada proceso en el grupo cgroup.
De esta manera, si este proceso siempre tiene solicitudes para procesar, es posible que otros procesos en el mismo grupo no se programen hasta que se agote la cuota de este cgroup. Esto hará que otros procesos del mismo grupo mueran de hambre y crearán un cuello de botella en el rendimiento de IO.
En este caso, podemos establecer slice_idle = 0 y group_idle = 8. De esta manera, la espera inactiva se realiza en unidades cgroup en lugar de unidades de proceso cfq_queue para evitar que ocurran los problemas anteriores.
low_latency: Este es un interruptor utilizado para activar o desactivar el modo de baja latencia de cfq.
Cuando este interruptor está activado, cfq volverá a calcular el tiempo de segmento de cada proceso en función de la configuración del parámetro target_latency.
Esto facilitará la equidad en el rendimiento (el valor predeterminado es la equidad en la asignación de intervalos de tiempo).
Al desactivar este parámetro (establecerlo en 0) se ignorará el valor target_latency. Esto permitirá que los procesos en el sistema asignen recursos de IO de una manera completamente por intervalos de tiempo. Este interruptor está activado de forma predeterminada.
Ya sabemos que el diseño cfq tiene el concepto de "inactivo". El propósito es fusionar tantas operaciones consecutivas de lectura y escritura como sea posible, reducir las operaciones de direccionamiento del cabezal magnético y aumentar el rendimiento. cantidad.
Si hay un proceso que siempre realiza lectura y escritura secuencial muy rápidamente, hará que la velocidad de respuesta de otros procesos que necesitan manejar IO disminuya debido a la alta tasa de aciertos en espera de inactividad de cfq. Si es necesario programar otro proceso, si el proceso no emite una gran cantidad de comportamientos de IO secuenciales, el rendimiento de IO de diferentes procesos en el sistema será muy desigual.
Por ejemplo, cuando hay muchas páginas sucias en la memoria caché del sistema que deben reescribirse y es necesario abrir un navegador en el escritorio para su funcionamiento, el comportamiento en segundo plano de reescribir páginas sucias es Es probable que provoque una gran cantidad de tiempos de inactividad, lo que hace que el navegador espere una pequeña cantidad de IO, lo que hace que el usuario sienta que la velocidad de respuesta del navegador es más lenta.
Esta baja latencia es principalmente una opción para optimizar esta situación. Cuando está activada, el sistema limitará los procesos que ocupan una gran cantidad de rendimiento de IO debido a visitas inactivas de acuerdo con la configuración de target_latency para lograrlo. diferentes procesos El rendimiento ocupado por IO está relativamente equilibrado. Este interruptor es más adecuado para activarlo en escenarios similares a las aplicaciones de escritorio.
target_latency: cuando el valor de low_latency está activado, cfq volverá a calcular la longitud del intervalo de tiempo de IO asignado a cada proceso en función de este valor.
quantum: este parámetro se utiliza para establecer cuántas solicitudes de IO se procesan desde cfq_queue cada vez. En un ciclo de eventos de procesamiento de cola, las solicitudes de E/S que superen este número no se procesarán. Este parámetro solo es válido para solicitudes sincrónicas.
slice_sync: cuando se programa el procesamiento de una cola cfq_queue, el tiempo total de procesamiento que se puede asignar se especifica mediante este valor como parámetro de cálculo. La fórmula es: time_slice = slice_sync + (slice_sync/5 * (4 - prio)). Este parámetro es válido para solicitudes sincrónicas.
slice_async: Este valor es similar al anterior, excepto que es válido para solicitudes asincrónicas.
slice_async_rq: este parámetro se utiliza para limitar el número máximo de solicitudes asincrónicas que una cola puede manejar dentro del rango de tiempo de un segmento. El número máximo de solicitudes procesadas también está relacionado con la prioridad io establecida por el proceso correspondiente.
Modo IOPS de 1.3 cfq
Ya sabemos que, de forma predeterminada, cfq admite la programación de prioridades en forma de intervalos de tiempo para garantizar la equidad en la ocupación de recursos de IO.
Los procesos de alta prioridad obtendrán intervalos de tiempo más largos, mientras que los procesos de baja prioridad obtendrán intervalos de tiempo relativamente más pequeños.
Cuando nuestro almacenamiento es un dispositivo de alta velocidad que admite NCQ (cola de comandos nativa), será mejor que le permitamos manejar múltiples solicitudes de múltiples colas de cfq para mejorar el rendimiento de NCQ.
En este momento, no es apropiado utilizar el método de asignación de intervalos de tiempo para asignar recursos, porque según la asignación de intervalos de tiempo, solo hay una cola de solicitudes que se puede procesar al mismo tiempo.
En este momento, necesitamos cambiar el modo cfq al modo IOPS. El método de cambio es muy simple, simplemente configure slice_idle = 0. El kernel detectará automáticamente si su dispositivo de almacenamiento es compatible con NCQ y, de ser así, cfq cambiará automáticamente al modo IOPS.
Además, en el modo de intervalo de tiempo basado en prioridad predeterminado, podemos usar el comando ionice para ajustar la prioridad de IO del proceso. La prioridad de IO predeterminada asignada a un proceso se calcula en función del valor agradable del proceso. El método de cálculo se puede ver en man ionice.
2. Fecha límite: programación de fechas límite
El algoritmo de programación de fechas límite es mucho más simple que cfq. Su objetivo de diseño es:
Si bien se garantiza que se acceda a las solicitudes en el orden de los sectores del dispositivo, otras solicitudes no mueren de hambre y deben programarse antes de una fecha límite.
Sabemos que la búsqueda del disco por el cabezal magnético puede realizar acceso secuencial y acceso aleatorio. Debido al tiempo de retardo de búsqueda, el rendimiento de IO es mayor durante el acceso secuencial y el rendimiento del acceso aleatorio es mayor. pequeño. .
Si queremos optimizar el rendimiento de un disco duro mecánico, podemos dejar que el programador clasifique las solicitudes de IO a las que se accede en el orden más complejo y luego envíe las solicitudes al disco duro en este orden, de modo que ese rendimiento de IO es mayor.
Pero hay otro problema con esto, es decir, si se produce una solicitud en este momento, la pista a la que quiere acceder está muy lejos de la pista donde se encuentra el cabezal actual, y una gran cantidad de las solicitudes de solicitudes se concentran cerca de la pista actual.
Como resultado, una gran cantidad de solicitudes siempre se fusionarán y se pondrán en cola, y la solicitud que necesita acceder a una pista más alejada morirá de hambre porque no se puede programar.
Deadline es un programador que puede intentar programar solicitudes remotas dentro de una fecha límite sin morir de hambre y al mismo tiempo garantizar el máximo rendimiento de E/S.