io_uring: la API asíncrona moderna del kernel Linux

io_uring: la API asíncrona moderna del kernel Linux

@programacion

Durante décadas, hacer I/O asíncrona en Linux fue un dolor de cabeza. epoll sirve para sockets pero no para archivos. aio (la vieja AIO de POSIX) prometía mucho pero entregaba poco: bloqueaba en operaciones reales, soportaba un puñado de syscalls y nadie quería usarla. En 2019, Jens Axboe (mantenedor del subsistema de bloque del kernel) introdujo io_uring, una nueva interfaz que rompió ese techo y hoy es la base de servidores web, bases de datos y runtimes modernos.

¿Qué es io_uring?

io_uring es una interfaz del kernel Linux que permite a una aplicación encolar operaciones de entrada/salida (leer un archivo, enviar un paquete por la red, abrir un socket) y recibir los resultados sin bloquear el hilo y sin pagar el costo de una llamada al sistema por cada operación. Apareció oficialmente en el kernel 5.1 (mayo 2019) y desde entonces se expandió hasta cubrir más de 60 operaciones distintas.

La idea clave: en lugar de que el programa le pida al kernel "haceme esto" una vez por operación, ambos comparten dos colas circulares en memoria. El programa escribe pedidos en una cola; el kernel los lee, los procesa y escribe los resultados en la otra. Así, miles de operaciones pueden viajar al kernel con una sola syscall, o incluso con cero si se activa el modo polling.

El problema que vino a resolver

Cada syscall en Linux moderno cuesta entre 100 y 1000 nanosegundos: el procesador tiene que cambiar de modo usuario a modo kernel, salvar registros, y desde Spectre/Meltdown también purgar parcialmente cachés y TLBs. En un servidor que mueve un millón de operaciones por segundo, eso son segundos enteros de CPU quemados solo en cambios de contexto.

Las alternativas previas no escalaban:

  • epoll — funciona para sockets y pipes, pero falla con archivos en disco (que siempre se reportan "listos" aunque la lectura bloquee).
  • Linux AIO (libaio) — solo soporta lecturas/escrituras directas con O_DIRECT, ignora el page cache y bloquea en metadata.
  • Threads — escalan mal: 10.000 conexiones = 10.000 stacks + 10.000 cambios de contexto.

Cómo funciona por dentro

io_uring se basa en dos buffers circulares (ring buffers) compartidos entre el espacio de usuario y el kernel mediante mmap:

  • Submission Queue (SQ) — la app escribe acá los pedidos (SQE: Submission Queue Entry).
  • Completion Queue (CQ) — el kernel deja acá los resultados (CQE: Completion Queue Entry).

El flujo es simple: la app prepara N pedidos en la SQ, llama a io_uring_enter() una sola vez para avisarle al kernel, y luego lee los resultados de la CQ cuando estén listos. Con SQ_POLL activado, ni siquiera hace falta esa syscall: un hilo del kernel hace polling de la cola constantemente.

Ejemplo mínimo en C

Con la librería oficial liburing (también de Axboe), leer un archivo asincrónicamente queda así:

#include <liburing.h>
#include <fcntl.h>
#include <stdio.h>

int main() {
struct io_uring ring;
io_uring_queue_init(8, &ring, 0);

int fd = open("datos.txt", O_RDONLY);
char buf[4096];

struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fd, buf, sizeof(buf), 0);
io_uring_submit(&ring);

struct io_uring_cqe *cqe;
io_uring_wait_cqe(&ring, &cqe);
printf("Leídos %d bytes\n", cqe->res);

io_uring_cqe_seen(&ring, cqe);
io_uring_queue_exit(&ring);
return 0;
}

Ese mismo patrón sirve para accept(), recv(), send(), openat(), statx() y muchas más. Y se pueden encadenar: "abrí este archivo, después leelo, después cerralo" como una sola operación atómica.

Quién lo está usando hoy

io_uring no es un experimento académico: ya forma parte del stack de producción de proyectos masivos.

  • ScyllaDB y Ceph — bases de datos NoSQL que mueven petabytes con latencias submicrosegundo.
  • Tokio (Rust) — el runtime async más popular de Rust integra io_uring vía tokio-uring.
  • Node.js y libuv — adoptaron io_uring en Linux 5.10+ para el filesystem.
  • QEMU — usa io_uring como backend de almacenamiento por defecto.
  • systemd-journald — escribe logs sin bloquear el bus principal.

El lado oscuro: seguridad

Tanta potencia trajo problemas. Entre 2022 y 2025 aparecieron varias vulnerabilidades graves en io_uring (escaladas a root vía race conditions y use-after-free), al punto de que Google prohibió su uso en Android y ChromeOS, y servicios endurecidos como los containers de producción de muchas empresas lo deshabilitan con sysctl kernel.io_uring_disabled=2. El kernel 6.0+ introdujo seccomp filters específicos para mitigar la superficie de ataque.

La lección: io_uring expone al kernel mucho más estado compartido que las syscalls tradicionales, y eso multiplica los caminos por donde un atacante puede colarse. Es una herramienta para servidores controlados, no para sandboxes.

¿Por qué importa?

io_uring es la primera revisión profunda del modelo de I/O de Linux desde los años 90. Ataca el problema en su raíz: el costo de cruzar la frontera kernel/usuario. Para servidores que necesitan exprimir hardware moderno (NVMe que entrega millones de IOPS, NICs de 100GbE, GPUs con DMA directo) las viejas APIs simplemente no alcanzan.

Si escribís código de alto rendimiento en Linux —bases de datos, proxies, motores de juegos en línea, sistemas de archivos distribuidos—, io_uring ya no es una opción exótica: es la API que viene a reemplazar a epoll y aio en la próxima década. Los runtimes async de Rust, Go y Node se están reescribiendo alrededor de ella, y los benchmarks suelen mostrar mejoras de 2x a 10x en throughput respecto a las APIs anteriores.

Conclusión

io_uring no es solo "otra API más" del kernel: es un cambio de paradigma. Pasamos de un modelo donde cada operación es una conversación corta entre app y kernel, a uno donde ambos comparten un espacio común y se sincronizan con anillos circulares y memoria compartida. El resultado: latencias más bajas, throughput más alto y un modelo de programación que finalmente trata a los archivos y a los sockets como ciudadanos de primera clase.

La curva de aprendizaje es real, y la superficie de ataque también. Pero para quien necesita hacer I/O en serio en Linux moderno, io_uring es el futuro —y el futuro ya lleva siete años corriendo en producción.

📖 Versión extendida con más detalle: https://elsolitario.org/2026/05/04/io-uring-api-asincrona-linux/?utm_source=telegraph&utm_medium=instant_view&utm_campaign=programacion

Report Page