--- title: "Cómo ajustar Linux para baja latencia en Ubuntu 24.04" description: "Reduce la latencia y el jitter en Ubuntu 24.04 con tuned, aislamiento de CPU, nohz_full, afinidad de IRQ y prioridad en tiempo real, midiendo con cyclictest." category: "Temas Avanzados" language: es source: https://cubepath.com/es/docs/temas-avanzados/ajuste-de-rendimiento-extremo-para-aplicaciones-de-baja-latencia updated: 2026-09-25 --- # Cómo ajustar Linux para baja latencia en Ubuntu 24.04 Algunas cargas de trabajo (trading, servidores de juego, VoIP, control industrial) no se miden por la latencia media sino por la peor: un pico de varios milisegundos cada pocos segundos basta para estropearlas. Esos picos suelen venir del propio sistema operativo: cambios de frecuencia de la CPU, interrupciones, el tick del planificador u otros procesos que se cuelan en el mismo núcleo. En este tutorial medirás la latencia de planificación de Ubuntu 24.04 con `cyclictest`, aplicarás un perfil de `tuned`, reservarás núcleos de CPU solo para tu aplicación, sacarás de ellos las interrupciones y ejecutarás la aplicación con prioridad de tiempo real, comprobando la mejora después de cada cambio. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS y un usuario no root con privilegios `sudo`. - Al menos 4 CPU, idealmente 8 o más: vas a reservar varias para la aplicación y dejar el resto para el sistema. - Acceso a la consola del servidor por si un parámetro de arranque impide el acceso por SSH. - Una aplicación que quieras optimizar. En los ejemplos se llama `/opt/miapp/bin/miapp`; sustitúyela por la tuya. > Nota: En un VPS los núcleos virtuales comparten hardware con otras máquinas y el hipervisor puede pausar la CPU virtual, así que los ajustes de esta guía reducen el jitter del sistema invitado pero no eliminan el del host. Para latencias estables por debajo de las decenas de microsegundos usa un servidor dedicado (bare metal) y ajusta también la BIOS: perfil de rendimiento, estados C profundos desactivados. Los ejemplos suponen 8 CPU (0 a 7), reservando las CPU 4 a 7 para la aplicación. Adapta los rangos a tu hardware. ## Paso 1: Instalar las herramientas de medición Instala `rt-tests` (que incluye `cyclictest`), `tuned` para aplicar perfiles de sistema y `numactl` para ver la topología de memoria: ```bash sudo apt update sudo apt install rt-tests tuned numactl ``` Revisa la topología de la CPU. Te interesa saber qué CPU lógicas son hilos del mismo núcleo físico (Hyper-Threading), porque conviene reservar núcleos completos: ```bash lscpu -e=CPU,CORE,SOCKET,NODE ``` ```output CPU CORE SOCKET NODE 0 0 0 0 1 1 0 0 2 2 0 0 3 3 0 0 4 0 0 0 5 1 0 0 6 2 0 0 7 3 0 0 ``` En este ejemplo las CPU 0 y 4 son el mismo núcleo físico. Si reservas la 4 pero dejas la 0 para el sistema, la carga del sistema seguirá afectando a tu aplicación a través del núcleo compartido. En ese caso, reserva pares completos (por ejemplo 2, 3, 6 y 7) o desactiva Hyper-Threading en la BIOS. ## Paso 2: Medir la latencia de partida Antes de cambiar nada, mide cuánto tarda el sistema en despertar un hilo de tiempo real. `cyclictest` crea un hilo por CPU, le pide dormir 200 microsegundos una y otra vez y registra cuánto se retrasa cada despertar: ```bash sudo cyclictest -m -S -p 80 -i 200 -D 5m -q ``` Las opciones hacen lo siguiente: `-m` bloquea la memoria en RAM, `-S` lanza un hilo fijado a cada CPU, `-p 80` usa prioridad de tiempo real 80, `-i 200` fija el intervalo en microsegundos, `-D 5m` hace que la prueba dure cinco minutos y `-q` muestra solo el resumen final. Genera una carga parecida a la real mientras se ejecuta, porque en reposo casi cualquier sistema da buenos números. ```output # /dev/cpu_dma_latency set to 0us T: 0 ( 2310) P:80 I:200 C:1499874 Min: 3 Act: 6 Avg: 5 Max: 412 T: 1 ( 2311) P:80 I:200 C:1499851 Min: 3 Act: 5 Avg: 5 Max: 389 ... T: 7 ( 2317) P:80 I:200 C:1499803 Min: 3 Act: 7 Avg: 6 Max: 531 ``` Los valores están en microsegundos. Fíjate sobre todo en `Max`: es la peor latencia observada y la que quieres reducir. Guarda esta salida para compararla al final. ## Paso 3: Aplicar el perfil network-latency de tuned `tuned` agrupa en perfiles los ajustes típicos de rendimiento. El perfil `network-latency` incluye `latency-performance` (gobernador de CPU en `performance` e impide estados de reposo profundos mediante `/dev/cpu_dma_latency`) y añade ajustes de red y memoria para baja latencia, como el sondeo activo de sockets y la desactivación del balanceo automático NUMA. Activa el servicio y el perfil: ```bash sudo systemctl enable --now tuned sudo tuned-adm profile network-latency ``` Comprueba que el perfil está activo y aplicado correctamente: ```bash tuned-adm active sudo tuned-adm verify ``` ```output Current active profile: network-latency Verification succeeded, current system settings match the preset profile. ``` Comprueba algunos de los valores que ha cambiado: ```bash sysctl kernel.numa_balancing net.core.busy_poll net.core.busy_read ``` ```output kernel.numa_balancing = 0 net.core.busy_poll = 50 net.core.busy_read = 50 ``` En un VPS es normal que `tuned` no pueda cambiar el gobernador de CPU, porque la máquina virtual no controla la frecuencia real del procesador. ## Paso 4: Aislar núcleos de CPU para la aplicación Por defecto el planificador reparte cualquier proceso en cualquier CPU, y cada CPU recibe un tick periódico del temporizador y trabajo del subsistema RCU. Tres parámetros de arranque del kernel eliminan esas fuentes de ruido en las CPU reservadas: | Parámetro | Efecto | |-----------|--------| | `isolcpus=managed_irq,domain,4-7` | El planificador no coloca procesos en las CPU 4-7 salvo que se fijen de forma explícita, y las interrupciones gestionadas por el kernel las evitan | | `nohz_full=4-7` | Detiene el tick del temporizador en esas CPU cuando solo ejecutan una tarea | | `rcu_nocbs=4-7` | Mueve los callbacks de RCU a las CPU del sistema | Además, el kernel genérico de Ubuntu 24.04 admite `preempt=full`, que hace el kernel totalmente expropiable y reduce el tiempo que una tarea de alta prioridad espera a que el kernel termine lo que estaba haciendo. Edita la configuración de GRUB: ```bash sudo nano /etc/default/grub ``` Añade los parámetros a `GRUB_CMDLINE_LINUX_DEFAULT`, conservando los que ya hubiera: ```ini GRUB_CMDLINE_LINUX_DEFAULT="isolcpus=managed_irq,domain,4-7 nohz_full=4-7 rcu_nocbs=4-7 preempt=full" ``` Regenera la configuración y reinicia: ```bash sudo update-grub sudo reboot ``` Tras el reinicio, comprueba que el kernel ha aplicado el aislamiento: ```bash cat /sys/devices/system/cpu/isolated /sys/devices/system/cpu/nohz_full ``` ```output 4-7 4-7 ``` Y que el modo de expropiación es `full`: ```bash sudo dmesg | grep -i 'dynamic preempt' ``` ```output [ 0.051234] Dynamic Preempt: full ``` > Consejo: Si necesitas garantías de tiempo real más estrictas, Ubuntu 24.04 ofrece un kernel PREEMPT_RT a través de Ubuntu Pro (gratuito para uso personal en hasta cinco máquinas). Se instala con `sudo pro attach` y `sudo pro enable realtime-kernel`. Repite la medición del paso 2 para comprobar si compensa en tu caso. ## Paso 5: Sacar las interrupciones de los núcleos aislados Las interrupciones de la tarjeta de red y del disco interrumpen lo que esté ejecutando la CPU que las atiende. `irqbalance` reparte las interrupciones entre CPU; en versiones recientes ya evita las CPU de `isolcpus` y `nohz_full`, pero conviene prohibirlas de forma explícita. Edita su configuración: ```bash sudo nano /etc/default/irqbalance ``` Añade esta línea: ```ini IRQBALANCE_BANNED_CPULIST=4-7 ``` Reinicia el servicio: ```bash sudo systemctl restart irqbalance ``` Comprueba a qué CPU va cada interrupción de la red. Primero localiza sus números de IRQ en `/proc/interrupts` (en un VPS suelen llamarse `virtio0-input.0` o similar): ```bash grep -E 'virtio|eth|ens|enp|mlx' /proc/interrupts | awk '{print $1, $NF}' ``` ```output 24: virtio0-config 25: virtio0-input.0 26: virtio0-output.0 ``` Consulta la afinidad efectiva de una de ellas: ```bash cat /proc/irq/25/effective_affinity_list ``` ```output 1 ``` Ninguna interrupción de dispositivo debería apuntar a las CPU 4 a 7. En la tabla de `/proc/interrupts`, los contadores de esas columnas deberían quedarse prácticamente quietos salvo en líneas como `LOC` o `RES`, que son internas del kernel. ## Paso 6: Ejecutar la aplicación en los núcleos reservados con prioridad de tiempo real Con las CPU aisladas, solo se ejecuta en ellas lo que fijes expresamente. La forma más fiable de hacerlo en producción es un servicio de systemd, que aplica afinidad, política de planificación y límites cada vez que arranca la aplicación. Crea la unidad: ```bash sudo nano /etc/systemd/system/miapp.service ``` Añade este contenido, ajustando la ruta y el usuario: ```ini [Unit] Description=Aplicación de baja latencia After=network-online.target Wants=network-online.target [Service] User=miapp ExecStart=/opt/miapp/bin/miapp CPUAffinity=4-7 CPUSchedulingPolicy=fifo CPUSchedulingPriority=80 LimitMEMLOCK=infinity Restart=on-failure [Install] WantedBy=multi-user.target ``` `CPUSchedulingPolicy=fifo` usa `SCHED_FIFO`: el hilo no cede la CPU hasta que se bloquea o aparece otro de mayor prioridad. `LimitMEMLOCK=infinity` permite a la aplicación llamar a `mlockall()` para que su memoria no se pagine a disco, lo que evitaría fallos de página en mitad de una operación. Carga la unidad y arranca el servicio: ```bash sudo systemctl daemon-reload sudo systemctl enable --now miapp ``` Comprueba en qué CPU se ejecutan los hilos y con qué política: ```bash ps -L -o tid,psr,cls,rtprio,comm -p "$(systemctl show -p MainPID --value miapp)" ``` ```output TID PSR CLS RTPRIO COMMAND 1843 4 FF 80 miapp 1851 5 FF 80 miapp ``` `PSR` es la CPU donde se está ejecutando el hilo y `FF` indica `SCHED_FIFO`. Si tu aplicación tiene varios hilos con funciones distintas, lo ideal es que ella misma fije cada hilo crítico a una CPU concreta (con `pthread_setaffinity_np()`) en lugar de dejar que el planificador los mueva entre la 4 y la 7. > Advertencia: No desactives el límite de tiempo real del kernel (`kernel.sched_rt_runtime_us`). Por defecto reserva un 5 % de cada segundo a tareas normales, lo que evita que un hilo `SCHED_FIFO` en bucle deje colgado el sistema. Para pruebas rápidas sin crear un servicio, puedes lanzar un proceso con la misma configuración usando `taskset` y `chrt`: ```bash sudo taskset -c 4-7 chrt -f 80 /opt/miapp/bin/miapp ``` ## Paso 7: Reducir la latencia de la tarjeta de red (opcional) Muchas tarjetas agrupan varias interrupciones de recepción para ahorrar CPU (interrupt coalescing), lo que añade decenas de microsegundos a cada paquete. Consulta la configuración actual, sustituyendo `eth0` por el nombre de tu interfaz (`ip -br link` lo muestra): ```bash sudo ethtool -c eth0 ``` Desactiva el ajuste adaptativo y la espera en recepción: ```bash sudo ethtool -C eth0 adaptive-rx off rx-usecs 0 ``` Si el comando responde `Operation not supported`, el driver no permite cambiar este valor, algo habitual en tarjetas virtuales. Si funciona, el cambio se pierde al reiniciar; para hacerlo permanente, crea una unidad de systemd: ```bash sudo nano /etc/systemd/system/nic-coalescing.service ``` Añade este contenido: ```ini [Unit] Description=Desactivar coalescing de interrupciones en eth0 After=network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/usr/sbin/ethtool -C eth0 adaptive-rx off rx-usecs 0 [Install] WantedBy=multi-user.target ``` Carga la unidad y actívala: ```bash sudo systemctl daemon-reload sudo systemctl enable --now nic-coalescing ``` En la aplicación, desactiva el algoritmo de Nagle en los sockets TCP con la opción `TCP_NODELAY`, que evita que el kernel retenga mensajes pequeños esperando a juntar más datos. ## Paso 8: Medir de nuevo en los núcleos aislados Detén tu aplicación para que no compita con la prueba y lanza `cyclictest` solo en las CPU reservadas, con un hilo por CPU: ```bash sudo systemctl stop miapp sudo cyclictest -m -p 80 -i 200 -D 5m -q -t 4 -a 4-7 ``` ```output # /dev/cpu_dma_latency set to 0us T: 0 ( 2604) P:80 I:200 C:1499990 Min: 2 Act: 3 Avg: 3 Max: 14 T: 1 ( 2605) P:80 I:200 C:1499989 Min: 2 Act: 3 Avg: 3 Max: 12 T: 2 ( 2606) P:80 I:200 C:1499987 Min: 2 Act: 3 Avg: 3 Max: 15 T: 3 ( 2607) P:80 I:200 C:1499986 Min: 2 Act: 2 Avg: 3 Max: 11 ``` Compara el `Max` con el del paso 2, con la misma carga en el resto del sistema. En un servidor dedicado bien ajustado es normal pasar de cientos de microsegundos a valores de un solo dígito o decenas bajas. En un VPS la mejora suele ser menor y los picos que quedan vienen del host. Cuando termines, vuelve a arrancar la aplicación: ```bash sudo systemctl start miapp ``` ## Solución de problemas - **`Max` sigue alto en un VPS.** Comprueba el tiempo robado por el hipervisor con `vmstat 1` (columna `st`). Si no es cero de forma sostenida, la latencia viene del host y solo la resuelve un servidor dedicado. - **Picos periódicos cada pocos segundos.** Busca procesos que se ejecuten en las CPU aisladas con `ps -eLo psr,comm | awk '$1>=4 && $1<=7'`. Además de tu aplicación, solo deberían aparecer hilos del kernel por CPU como `ksoftirqd/4`, `migration/4` o `cpuhp/4`. - **El sistema va lento tras el aislamiento.** Todo lo demás se ejecuta ahora en las CPU 0 a 3. Si se quedan cortas, reserva menos CPU para la aplicación. - **`tuned-adm verify` falla.** Revisa `/var/log/tuned/tuned.log`: suele indicar qué ajuste no se ha podido aplicar, normalmente el gobernador de CPU en máquinas virtuales. - **La aplicación no arranca con `SCHED_FIFO`.** Revisa `journalctl -u miapp`. Si el error es de permisos al fijar la política, asegúrate de que la usas desde la unidad de systemd y no desde el propio programa sin privilegios. ## Conclusión Has medido la latencia de planificación de Ubuntu 24.04, has aplicado el perfil `network-latency`, has reservado núcleos para la aplicación sin tick ni interrupciones y la ejecutas con prioridad de tiempo real, comprobando la mejora con `cyclictest`. Como siguientes pasos, reserva huge pages para reducir fallos de TLB, fija los hilos críticos de la aplicación a CPU concretas desde el código y, si tu carga es de red, valora el bypass del kernel con DPDK.