Linux io_uring 固定缓冲区适合什么 IO 场景
来源:17golang原创
时间:2026-10-09 10:05:46 220浏览 收藏
io_uring 固定缓冲区最适合少量、长期存在、会被高频重复使用的 I/O 缓冲池。它在注册时把用户内存映射到内核,并让后续 READ_FIXED/WRITE_FIXED 请求通过 buf_index 复用这份映射,从而避免每次 I/O 都做映射、解除映射和页引用计数操作。它常与 O_DIRECT 文件读写配合,但并不适合一次性 I/O、缓冲区尺寸频繁变化或内存紧张的进程。
liburing 官方项目:https://github.com/axboe/liburing
- 高 IOPS、固定大小块、缓冲池长期复用:优先评估固定缓冲区。
- 一次性命令行工具、低频文件读取、每次都分配不同内存:普通缓冲区通常更简单。
- 网络接收时提交请求前不知道该用哪块空闲内存:考虑 provided buffer ring,而不是把 fixed buffer 硬套进去。
- 是否更快必须用真实队列深度、块大小和设备测量,注册本身有一次性成本且会锁定内存。
哪些任务值得使用固定缓冲区
固定缓冲区的收益来自“注册一次、重复使用”。因此首先看任务,而不是只看 API 是否可用。
| IO 场景 | 适合程度 | 原因 |
|---|---|---|
| 存储服务反复读写固定大小数据块 | 适合 | 缓冲区池稳定,注册成本可被大量 I/O 摊薄 |
| 数据库页、缓存页或日志块的长期缓冲池 | 适合 | 页面会重复使用,索引与生命周期容易管理 |
O_DIRECT 高并发文件 I/O | 常见搭配 | 官方 liburing 文档明确指出可避免每次 I/O 的映射/解除映射和页引用操作 |
| 只读取一次的小文件 | 通常不适合 | 注册、内存管理和清理复杂度可能高于收益 |
| 请求尺寸与内存地址高度动态 | 谨慎 | 固定槽位难复用,更新注册表会增加管理成本 |
| 内存预算很紧的进程 | 谨慎 | 注册内存会被锁定,并计入相应的内存锁定限制 |
固定缓冲区到底固定了什么
io_uring_register_buffers() 接收一个 iovec 数组,把每个元素对应的用户内存登记到当前 ring。注册完成后,iovec 数组本身可以释放;但 iov_base 指向的真实内存仍必须保持有效,直到不再有请求使用并完成注销。
提交固定读写请求时,应用同时传入内存地址、长度和 buf_index。地址与长度必须落在该索引对应的已注册区域内,但地址不要求等于区域起点,因此可以读取注册区域中的一个子范围。

最小实现:注册一块内存并提交 READ_FIXED
下面示例注册一个 4 KiB 缓冲区,从普通文件偏移 0 读取数据。为了让代码重点落在 fixed buffer 关系上,示例没有启用 O_DIRECT;若生产环境启用它,还必须遵守文件系统和设备的地址、长度与偏移对齐要求。
#include#include #include #include #include #include #include #include #define QUEUE_DEPTH 32 #define BLOCK_SIZE 4096 int main(void) { struct io_uring ring; struct io_uring_sqe *sqe; struct io_uring_cqe *cqe; struct iovec iov; void *buffer = NULL; int fd = -1; int ret; ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0); if (ret res res)); else printf("读取完成:%d 字节\n", cqe->res); io_uring_cqe_seen(&ring, cqe); cleanup: close(fd); /* 先确保请求已经完成并注销,再释放底层用户内存。 */ io_uring_unregister_buffers(&ring); free(buffer); io_uring_queue_exit(&ring); return ret
# 使用 pkg-config 取得 liburing 的头文件和链接参数。 cc -O2 -Wall fixed-read.c -o fixed-read $(pkg-config --cflags --libs liburing) # 运行示例;请先准备当前目录下的 data.bin。 ./fixed-read
内存生命周期与错误处理最容易出错
固定缓冲区不是把内存复制给内核,而是建立长期映射。生产代码要把下面几条当成资源契约:
- 注册成功后可以销毁
iovec描述数组,但不能提前free(iov_base)。 buf_index必须指向对应注册槽,buf和nbytes必须完全落在该槽范围内。- 注册内存会被锁定,并受
RLIMIT_MEMLOCK等资源限制影响;大池子可能注册失败。 - 完成队列里的
cqe->res小于 0 时,值本身就是负的 errno,不能再读取全局errno判断该次异步 I/O。 - 注销或替换缓冲区前,要明确哪些请求仍在使用旧槽位,避免把生命周期管理交给运气。
性能检查要比较什么
固定缓冲区减少的是每次 I/O 的内存映射与页引用管理成本,不会自动消除设备延迟、文件系统开销或队列设计问题。建议在同一块大小、队列深度、设备和读写比例下,对比普通 io_uring_prep_read() 与 io_uring_prep_read_fixed():
| 指标 | 要回答的问题 |
|---|---|
| IOPS / 吞吐 | 复用注册映射后是否真的增加完成量 |
| p50 / p99 延迟 | 高并发下尾延迟是否改善 |
| CPU 时间 | 每次 I/O 的内存管理开销是否下降 |
| 注册与注销耗时 | 一次性成本需要多少请求才能摊薄 |
| 锁定内存量 | 性能收益是否挤压了进程或系统内存预算 |
如果只提交几十次 I/O,注册成本很可能占据明显比例;如果缓冲池长期服务数百万次请求,固定映射才有机会体现价值。具体分界线由设备、内核、块大小和负载决定,不能用一个通用数字代替基准测试。
固定缓冲区和 provided buffer ring 不要混用概念
fixed buffer 的核心是:应用在提交请求时已经知道要用哪块已注册内存,因此明确给出 buf_index。provided buffer ring 则用于另一类任务:应用把一组空闲缓冲区交给 ring,请求设置 IOSQE_BUFFER_SELECT,等接收数据时由内核选择一个可用缓冲区,并通过 CQE 标记返回具体缓冲区 ID。

| 机制 | 提交时谁选缓冲区 | 典型任务 |
|---|---|---|
| 普通缓冲区 | 应用直接传地址 | 低频、一次性、动态内存 I/O |
| 固定缓冲区 | 应用传地址、长度和 buf_index | 高频文件或块 I/O、稳定缓冲池 |
| provided buffer ring | 内核从缓冲组中选择 | 网络接收、多请求等待可用接收缓冲区 |
常见问题
固定缓冲区等于零拷贝吗?
不等于。它优化的是用户内存注册、映射和页引用管理路径;具体 I/O 是否发生数据复制取决于操作类型、设备和协议,不能把 fixed buffer 直接宣传成所有场景的零拷贝。
固定缓冲区只能配合 O_DIRECT 吗?
不是。普通文件 I/O 也可以使用 fixed read/write,但官方文档特别指出它与 O_DIRECT 配合时很有价值。是否值得使用仍应由真实负载测量。
可以在一块注册内存中读取子区域吗?
可以。buf 不必等于注册区域起点,但 buf 到 buf + nbytes 必须完全位于 buf_index 指定的区域内。
缓冲池需要动态扩缩怎么办?
可以研究稀疏注册与 buffer update 接口,把空槽后续更新为真实缓冲区。但动态更新会增加资源与并发管理复杂度,若地址变化非常频繁,普通缓冲区或 provided buffer ring 可能更合适。
-
227 收藏
-
180 收藏
-
331 收藏
-
386 收藏
-
261 收藏
-
233 收藏
-
179 收藏
-
文章 · linux | 19小时前 | 容器 · Linux · 容器隔离 mount namespace PID namespace linux namespace network namespace268 收藏
-
490 收藏
-
297 收藏
-
398 收藏
-
292 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习