当前位置:首页 > 文章列表 > 文章 > linux > Linux io_uring 固定缓冲区适合什么 IO 场景

Linux io_uring 固定缓冲区适合什么 IO 场景

来源:17golang原创 2026-10-09 10:05:46 0浏览 收藏

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。地址与长度必须落在该索引对应的已注册区域内,但地址不要求等于区域起点,因此可以读取注册区域中的一个子范围。

用户缓冲池、iovec 注册表、内核长期映射、buf_index 与 READ_FIXED WRITE_FIXED 请求之间边界关系的静态结构图
图1:固定的是 ring 对用户内存的注册关系;请求用 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。

固定缓冲区、普通缓冲区与 provided buffer ring 按内存复用、提交时是否知道槽位和生命周期特征划分的静态决策图
图2:已知槽位并反复复用时选 fixed buffer;接收时才需要空闲槽位时评估 provided buffer ring。这是静态决策图,不是性能结果截图。
机制提交时谁选缓冲区典型任务
普通缓冲区应用直接传地址低频、一次性、动态内存 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 可能更合适。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Python sqlite3 备份进度回调怎样判断剩余页数Python sqlite3 备份进度回调怎样判断剩余页数
上一篇
Python sqlite3 备份进度回调怎样判断剩余页数
对象仍被使用时 weak 指针为什么可能已经失效
下一篇
对象仍被使用时 weak 指针为什么可能已经失效
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    386次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    468次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    475次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    415次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    241次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码