--- title: "Elegir y ajustar el programador de E/S en Ubuntu 24.04" description: "Compara los programadores de E/S none, mq-deadline, bfq y kyber en Ubuntu 24.04, mide cada uno con fio y deja el elegido fijo con una regla de udev persistente." category: "Rendimiento y Benchmarking" language: es source: https://cubepath.com/es/docs/rendimiento-y-benchmarking/seleccion-y-ajuste-del-programador-de-es updated: 2026-09-25 --- # Elegir y ajustar el programador de E/S en Ubuntu 24.04 El programador de E/S (I/O scheduler) es la parte del kernel que decide en qué orden se envían al disco las peticiones de lectura y escritura. En discos mecánicos, reordenar y agrupar peticiones marca una gran diferencia; en NVMe y en discos virtuales, casi cualquier trabajo extra solo añade latencia. En este tutorial verás qué programadores ofrece Ubuntu 24.04, cambiarás el de un disco en caliente, medirás el efecto con `fio` y dejarás la elección fija con una regla de udev. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS o un servidor bare metal de CubePath. - Un usuario no root con privilegios `sudo`. - Unos 4 GB libres en el disco que vas a probar, para el archivo de pruebas de `fio`. ## Paso 1: Identificar los discos y el programador actual Lista los discos físicos o virtuales del servidor. La columna `ROTA` vale 1 para discos mecánicos y 0 para SSD, NVMe y la mayoría de discos virtuales: ```bash lsblk -d -o NAME,ROTA,SIZE,MODEL ``` ```output NAME ROTA SIZE MODEL sda 1 7.3T ST8000NM017B nvme0n1 0 894.3G SAMSUNG MZ1L2960HCJR ``` En un VPS verás normalmente un disco `vda` (virtio-blk) o `sda` (virtio-scsi) con `ROTA` a 0 o 1 según lo que informe el hipervisor. Consulta el programador de cada disco. El que está entre corchetes es el activo: ```bash grep . /sys/block/*/queue/scheduler ``` ```output /sys/block/nvme0n1/queue/scheduler:[none] mq-deadline /sys/block/sda/queue/scheduler:[mq-deadline] none ``` Ubuntu 24.04 usa por defecto `none` en dispositivos con varias colas de hardware (NVMe, la mayoría de discos virtio) y `mq-deadline` en el resto. Solo aparecen los programadores cuyo módulo está cargado; `bfq` y `kyber` se cargan bajo demanda. ## Paso 2: Conocer los programadores disponibles Los programadores antiguos (`noop`, `deadline`, `cfq`) desaparecieron en el kernel 5.0 junto con la capa de bloques de una sola cola. Si una guía te pide escribir `cfq` o `noop`, no funcionará en Ubuntu 24.04. Los actuales son: | Programador | Qué hace | Indicado para | |---|---|---| | `none` | Envía las peticiones en orden de llegada, sin reordenar. Mínimo uso de CPU. | NVMe, SSD rápidos, discos virtuales y RAID por hardware con caché. | | `mq-deadline` | Agrupa peticiones y asigna a cada una un plazo, dando prioridad a las lecturas. | Discos mecánicos y SSD SATA con cargas de servidor. | | `bfq` | Reparte el ancho de banda de forma justa entre procesos y prioriza la interactividad. Consume más CPU por petición. | Discos lentos compartidos por muchos procesos, escritorios. | | `kyber` | Ajusta la profundidad de las colas para cumplir un objetivo de latencia de lectura y escritura. | Dispositivos rápidos donde importa la latencia de cola bajo carga mixta. | Para cargar los módulos de `bfq` y `kyber` y que aparezcan en la lista: ```bash sudo modprobe bfq sudo modprobe kyber-iosched cat /sys/block/sda/queue/scheduler ``` ```output [mq-deadline] kyber bfq none ``` > Nota: en un VPS, el hipervisor ya planifica las E/S hacia los discos físicos. Añadir otro programador dentro de la máquina virtual rara vez ayuda, así que `none` suele ser la mejor opción. Mídelo antes de cambiarlo. ## Paso 3: Medir con fio Cambiar de programador sin medir es adivinar. `fio` genera cargas de E/S reproducibles e informa de IOPS y percentiles de latencia: ```bash sudo apt update sudo apt install fio ``` Crea un archivo de trabajo en `/etc/fio/` que simule una carga típica de base de datos: 70 % lecturas y 30 % escrituras aleatorias de 4 KB, saltándose la caché de página con `direct=1` para medir el disco y no la RAM: ```bash sudo mkdir -p /etc/fio sudo nano /etc/fio/mixto.fio ``` ```ini [global] ioengine=libaio direct=1 bs=4k size=4G runtime=60 time_based=1 group_reporting=1 filename=/var/tmp/fio.test [mixto] rw=randrw rwmixread=70 iodepth=32 numjobs=4 ``` Cambia `filename` si el disco que quieres probar no contiene `/var/tmp` (por ejemplo, `/srv/datos/fio.test` para un disco montado en `/srv/datos`). Ejecuta la prueba con el programador actual: ```bash sudo fio /etc/fio/mixto.fio ``` De toda la salida, fíjate en las líneas de IOPS y en los percentiles de latencia de lectura: ```output read: IOPS=11.8k, BW=46.2MiB/s (48.4MB/s)(2771MiB/60001msec) write: IOPS=5071, BW=19.8MiB/s (20.8MB/s)(1189MiB/60001msec) clat percentiles (usec): | 1.00th=[ 202], 5.00th=[ 297], 10.00th=[ 375], 20.00th=[ 553], | 50.00th=[ 1336], 90.00th=[ 4555], 95.00th=[ 6456], | 99.00th=[12125], 99.50th=[15270], 99.90th=[24249] ``` Anota los IOPS y los percentiles 50 y 99. Estos valores son un ejemplo; los tuyos dependerán del disco. ## Paso 4: Cambiar el programador en caliente y comparar Escribe el nombre del programador en el archivo `scheduler` del disco. El cambio es inmediato, no requiere desmontar nada y se pierde al reiniciar: ```bash echo bfq | sudo tee /sys/block/sda/queue/scheduler cat /sys/block/sda/queue/scheduler ``` ```output bfq mq-deadline kyber [bfq] none ``` Repite la prueba del paso 3 con cada candidato (`none`, `mq-deadline`, `bfq`, `kyber`) y compara las cifras. Como orientación, en pruebas así suelen salir estos patrones: - **NVMe**: `none` da los mejores IOPS. `mq-deadline` y `kyber` quedan cerca; `bfq` pierde claramente por el coste de CPU. - **Disco mecánico**: `mq-deadline` y `bfq` superan a `none` porque ordenan las peticiones y reducen los movimientos del cabezal. `bfq` mejora la latencia cuando varios procesos compiten por el disco. - **Disco virtual**: las diferencias suelen ser pequeñas y `none` gana o empata. Si dos programadores dan cifras similares, quédate con el más simple. ### Ajustar los parámetros de mq-deadline Cada programador expone parámetros en `/sys/block//queue/iosched/`. Para `mq-deadline`, los más útiles son `read_expire` y `write_expire` (plazo máximo en milisegundos antes de atender una lectura o escritura, 500 y 5000 por defecto) y `writes_starved` (cuántas tandas de lecturas se atienden antes de dar paso a las escrituras pendientes, 2 por defecto): ```bash echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler grep . /sys/block/sda/queue/iosched/{read_expire,write_expire,writes_starved,fifo_batch} ``` ```output /sys/block/sda/queue/iosched/read_expire:500 /sys/block/sda/queue/iosched/write_expire:5000 /sys/block/sda/queue/iosched/writes_starved:2 /sys/block/sda/queue/iosched/fifo_batch:16 ``` Por ejemplo, para reducir la latencia de lectura en un disco mecánico con una base de datos, prueba a bajar `read_expire`: ```bash echo 250 | sudo tee /sys/block/sda/queue/iosched/read_expire ``` Vuelve a medir con `fio`. Si el percentil 99 de lectura no mejora, restaura el valor por defecto. ### Revisar la lectura anticipada Independientemente del programador, `read_ahead_kb` controla cuántos datos lee el kernel por adelantado en accesos secuenciales. El valor por defecto es 128 KB: ```bash cat /sys/block/sda/queue/read_ahead_kb ``` Subirlo (por ejemplo a 1024) ayuda en discos mecánicos que sirven archivos grandes de forma secuencial, como vídeo o copias de seguridad. En cargas aleatorias de base de datos no aporta nada o empeora, porque lee datos que nadie va a usar. ## Paso 5: Hacer el cambio persistente con udev El antiguo parámetro de arranque `elevator=` ya no tiene efecto en kernels con la capa de bloques multicola, así que la forma correcta de fijar un programador es una regla de udev. Crea el archivo: ```bash sudo nano /etc/udev/rules.d/60-ioscheduler.rules ``` Este ejemplo asigna `mq-deadline` a los discos mecánicos y `none` a los NVMe y demás discos no rotacionales. Adáptalo al resultado de tus pruebas: ```text # Discos mecánicos ACTION=="add|change", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd[a-z]*", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="mq-deadline" # SSD SATA y discos virtuales no rotacionales ACTION=="add|change", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd[a-z]*|vd[a-z]*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none" # NVMe ACTION=="add|change", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="nvme[0-9]*n[0-9]*", ATTR{queue/scheduler}="none" ``` La condición `ENV{DEVTYPE}=="disk"` excluye las particiones, que no tienen programador propio. Si elegiste `bfq` o `kyber`, añade también el módulo a `/etc/modules-load.d/` para que esté cargado cuando se aplique la regla: ```bash echo bfq | sudo tee /etc/modules-load.d/bfq.conf ``` Recarga las reglas y aplícalas a los discos existentes sin reiniciar: ```bash sudo udevadm control --reload-rules sudo udevadm trigger --subsystem-match=block --action=change ``` Comprueba el resultado: ```bash grep . /sys/block/*/queue/scheduler ``` ```output /sys/block/nvme0n1/queue/scheduler:[none] mq-deadline kyber bfq /sys/block/sda/queue/scheduler:[mq-deadline] kyber bfq none ``` Para confirmar que la regla sobrevive a un reinicio, reinicia el servidor en una ventana de mantenimiento y vuelve a ejecutar el mismo comando. Los ajustes de `iosched/` como `read_expire` se pueden persistir de la misma forma, añadiendo por ejemplo `ATTR{queue/iosched/read_expire}="250"` al final de la regla del disco correspondiente, siempre después de la asignación del programador. ## Paso 6: Vigilar el disco en producción Las pruebas sintéticas no sustituyen a la carga real. `iostat`, del paquete `sysstat`, muestra la latencia media y la ocupación de cada disco: ```bash sudo apt install sysstat iostat -dx 5 ``` La salida real tiene más columnas; estas son las relevantes: ```output Device r/s w/s rkB/s wkB/s r_await w_await aqu-sz %util nvme0n1 812.4 305.2 3249.6 4880.0 0.18 0.05 0.17 14.20 sda 96.0 41.8 6144.0 2140.0 7.95 12.40 1.28 78.60 ``` - `r_await` y `w_await`: tiempo medio en milisegundos de cada lectura y escritura, incluyendo el tiempo en cola. Es la cifra que el programador influye directamente. - `aqu-sz`: número medio de peticiones en cola. - `%util`: porcentaje de tiempo con alguna petición en curso. En discos mecánicos, valores cercanos al 100 % indican saturación; en NVMe no es fiable porque atiende muchas peticiones en paralelo. Compara estas cifras antes y después de cambiar el programador, en horas de carga similares. ## Solución de problemas **`tee: /sys/block/sda/queue/scheduler: Invalid argument`**: el nombre no existe o su módulo no está cargado. Ejecuta `sudo modprobe bfq` o `sudo modprobe kyber-iosched` y vuelve a intentarlo. Recuerda que `cfq`, `noop` y `deadline` ya no existen. **La regla de udev no se aplica tras reiniciar**: comprueba la sintaxis con `udevadm test /sys/block/sda 2>&1 | grep -i scheduler` y revisa que el nombre del disco coincide con el patrón `KERNEL==`. En discos virtio el nombre es `vda`, no `sda`. **Los resultados de fio varían mucho entre ejecuciones**: aumenta `runtime` a 120 segundos y repite cada prueba tres veces. En un VPS, otras máquinas del mismo host también afectan a las cifras. ## Conclusión Has identificado el programador de E/S de cada disco, comparado los disponibles con una carga reproducible de `fio` y fijado la elección con una regla de udev, sin depender del obsoleto parámetro `elevator=`. En la mayoría de servidores modernos la respuesta será `none` para NVMe y discos virtuales y `mq-deadline` para discos mecánicos, pero ahora tienes datos para confirmarlo. Como siguientes pasos, puedes borrar el archivo de pruebas con `sudo rm /var/tmp/fio.test`, ajustar las opciones de montaje del sistema de archivos (`noatime`) o revisar los parámetros `vm.dirty_*` del kernel para controlar cómo se vuelcan las escrituras al disco.