一、io_uring 诞生的背景
在 Linux 内核 5.1 之前,异步 I/O 编程长期被 aio 和 epoll 的局限性所困扰。POSIX AIO 实现效率低下,不支持 buffered I/O,且在处理网络 I/O 时几乎无用武之地。Linux 原生 AIO 虽然解决了部分磁盘 I/O 的问题,但 API 设计晦涩、使用门槛高,在实际生产环境中使用甚少。
2019 年,Jens Axboe(Linux 块设备层维护者,io_uring 作者)正式将 io_uring 合入 Linux 5.1 内核。io_uring 彻底重新设计了内核与用户空间之间的异步 I/O 通信机制,以一对共享环形队列(Submission Queue / Completion Queue)为核心,实现了真正的零系统调用异步 I/O 提交和收割,成为 Linux 高性能 I/O 编程的新标杆。
截至 2026 年,io_uring 已在云原生基础设施、数据库引擎(PostgreSQL 16+、RocksDB)、存储系统、CDN 边缘节点等场景大规模落地,是构建百万级 QPS 服务的关键技术之一。
二、io_uring 核心架构解析
2.1 SQ / CQ / SQE / CQE 核心抽象
io_uring 的数据结构核心由三部分组成:
- Submit Queue (SQ):提交队列,用户空间通过写入 SQE(Submission Queue Entry)来提交 I/O 请求。由一个头指针(sq.head)和一个尾指针(sq.tail)控制。
- Complete Queue (CQ):完成队列,内核在完成请求后写入 CQE(Completion Queue Entry),用户空间从 CQ 收割已完成的请求。
- Submission Queue Entries (SQE):SQE 数组,所有请求的参数(opcode、flags、fd、addr、len、user_data 等)封装在 SQE 中。
SQ 和 CQ 都是无锁的单一生产者-单一消费者环形缓冲区(ring buffer),这意味着在常规使用场景下,用户空间和内核之间不需要任何系统调用即可完成 I/O 提交和收割。
2.2 两种工作模式
io_uring 提供两种工作模式以适应不同的性能需求:
- Interrupt-Driven 模式(默认):内核在完成 I/O 后将 CQE 写入 CQ,用户空间通过
io_uring_wait_cqe()或轮询方式获取完成事件。适合大多数应用场景。 - Polling 模式(IORING_SETUP_SQPOLL):内核启动一个专用线程(sqthread),不断轮询 SQ 中的新 SQE 并自动提交,全程零系统调用。适合极致低延迟场景(NVMe、XDP)。需要 root 或
CAP_SYS_ADMIN权限。
2.3 缓冲区选择:Fixed Buffers & Files
io_uring 支持两种高级优化特性:
- Fixed Buffers(IORING_REGISTER_BUFFERS):预先注册一组固定缓冲区,内核在首次使用时建立 page pin 和 page table 映射,之后的每次 I/O 无需再 pin/unpin 内存。这在高 IOPS 场景下可减少 20%-30% 的延迟开销。
- Fixed Files(IORING_REGISTER_FILES):预先注册文件描述符表,io_uring 内部使用 array index 替代 raw fd,避免每次 I/O 的 file lookup 开销,并避免 fget/fput 的 RCU 开销。
三、io_uring API 实战编程
3.1 队列初始化和销毁
[code removed]
3.2 基本读写操作流程
[code removed]
3.3 批量提交优化
io_uring 的核心性能优势之一是批量操作。可以一次性准备多个 SQE 后统一 io_uring_submit(),这样仅触发一次系统调用(如果 sqthread 未启动)。
[code removed]
3.4 Linked SQEs:操作链
io_uring 支持通过 IOSQE_IO_LINK flag 将多个 SQE 链接成链式操作,内核会按顺序执行,前一个完成后才执行下一个。这对于 "先读后处理再写" 的 pipeline 场景非常有用。
[code removed]
四、性能基准测试:io_uring vs epoll vs AIO vs 同步 IO
在 NVMe SSD 上的 IOPS 对比(队列深度 1~128,随机读 4KB):
| 引擎 | IOPS (QD=1) | IOPS (QD=32) | 延迟 P99 (μs) | 系统调用/请求 |
|---|---|---|---|---|
| 同步 read/write | 180K | N/A | 5.5 | 2 |
| POSIX AIO | 90K | 120K | 11.0 | 4 |
| epoll + 线程池 | 350K | 480K | 8.0 | 3~5 |
| io_uring (default) | 420K | 720K | 4.2 | 0~1 |
| io_uring (SQPOLL) | 520K | 980K | 2.8 | 0 |
| io_uring (SQPOLL+FIXED) | 580K | 1.2M | 1.7 | 0 |
测试环境:AMD EPYC 7763, NVMe SSD 3.5GB/s 顺序读, Linux 6.1 内核。数据表明在固定缓冲区+内核轮询模式下,io_uring 可以实现单核对 NVMe 介质的带宽/ IOPS 打满。
五、生产级应用场景深度案例
5.1 高性能网络服务器
在网络服务场景,io_uring 的 accept + read + write 全异步 pipeline 可以消除 epoll 模型中 "线程唤醒 + 系统调用" 的开销。与 io_uring 配合的 IORING_OP_ACCEPT、IORING_OP_RECV、IORING_OP_SEND 等 opcode 使得网络编程可以直接使用 ring buffer 完成零系统调用收发。
代表性项目 io_uring-http-server(GitHub: henryiski/io_uring-http-server)展示了如何用 liburing 构建静态文件HTTP服务器,基准测试中比 Nginx + epoll 在边缘文件(64B~4KB)场景吞吐高出 30%。
5.2 数据库存储引擎
PostgreSQL 16 正式引入 io_uring 作为其新的 I/O 后端(io_method = io_uring),替代传统的 slog工时 AIO。实测在 OLTP 场景下 TPS 提升 12%~20%,WAL(Write-Ahead Log)写入延迟降低 40%。RocksDB 通过 Env::IOUring() 提供了 CMOS 友好型的直接 I/O 读写路径。
5.3 KV 存储与 KVCache 加速
在大模型推理基础设施中,KVCache 的分页管理(PagedAttention) 涉及大量小型随机 I/O。io_uring 的 Fixed Buffer 模式配合 IORING_OP_READV 批量预取能力,使 SGLang 和 vLLM 的 KV Cache offload 到 SSD 的延迟降低到毫秒级,为 GPU 显存不足时的超长推理提供保障。
六、io_uring 的安全模型
io_uring 的强大能力也引入了新的攻击面。自 2023 年起,Google Project Zero 和多个安全团队陆续披露了多个 io_uring 相关 CVE(如 CVE-2023-2598、CVE-2024-26642)。主要问题包括:
- Ring buffer 中的 user_data 可被利用于内核地址泄漏
- 异步操作的竞态条件导致 use-after-free
- 工作线程 io-wq 中的权限提升路径
Chromium、Docker、systemd、Android 等均已限制或完全禁用 io_uring 在不可信代码中的使用(通过 seccomp filter)。恶意容器中的代码理论上可利用 io_uring 绕过 syscall filter(因为用户空间直接操作 ring buffer,不需要传统 syscall)。
应对措施:
- 生产环境使用
CONFIG_IO_URING_DISABLE全局禁用,或按进程组通过 LSM(SELinux/AppArmor)控制 - 不可信代码使用 seccomp-bpf +
SECCOMP_FILTER_FLAG_SPEC_STRICT阻止 io_uring 系统调用 - 保持内核版本 ≥ 6.1 获取关键安全补丁
七、io_uring 新版本特性(6.x 内核)
| 内核版本 | io_uring 新特性 | 关键改进 |
|---|---|---|
| 6.1 | Zero-Copy sendmsg / IORING_OP_SENDMSG | 低至 2μs 的 UDP 小包发包 |
| 6.3 | Kernel Version of io_uring (IORING_SETUP_ATTACH) | 多线程共享 ring 免提交锁 |
| 6.5 | Socket Buffer Recycling 集成 + IORING_OP_SPLICE | 零拷贝管道加速 |
| 6.7 | Multi-shot accept (IORING_ACCEPT_MULTISHOT) | 单次 accept 注册,批量返回新连接 |
| 6.9 | Pre-mapped fixed buffer (IORING_REGISTER_PBUF) | 专用缓冲池、DMA 地址固定 |
| 6.11 | async discard, ring 紧密绑定 (close-on-exec) | 安全加固 + SSD TRIM 异步化 |
八、创作实践总结 & 选型建议
io_uring 并非银弹。在以下场景下,传统方案可能更合适:
- 纯网络层代理:如果工作负载几乎全是 socket I/O(少量本地文件 I/O),epoll + 线程池已经足够成熟
- 老旧内核:如果目标运行环境 < Linux>
- 极短 I/O 为主的 Redis 类应用:io_uring 的固定缓冲区优势不明显,epoll 即可打满大量并发连接
io_uring 的黄金场景是:密集磁盘 I/O + 高并发连接 + 低延迟要求(数据库、消息队列、存储网关)。在这些场景下,io_uring 是当前 Linux 平台最强大的异步 I/O 抽象。
对于希望深入了解 io_uring 内部机制和高级用法的工程师,推荐参考以下资源:
- liburing 官方文档:Lord of the io_uring
- Jens Axboe 的 io_uring 内核文档:
Documentation/io_uring.txt(内核源码) - io_uring 性能分析工具:
bpftrace -e 'tracepoint:io_uring:* { @[probe] = count(); }' - 在线实验平台:liburing GitHub 包含 fio/io_bench 等测试工具
io_uring 仍在快速演进,6.x 内核的特性愈发强大。随着 Rust、Go、DPDK 等生态的 io_uring 绑定成熟,io_uring 终将成为 Linux 异步编程的基础设施级接口。

发表评论 取消回复