当前位置: 首页 > news >正文

io_uring 避坑指南

io_uring 是 Linux 内核 5.1(2019年5月发布)引入的高性能异步 I/O 框架,其主要特点如下:

✅ 真正零拷贝、无锁、高效异步 I/O;

✅ 一次系统调用提交多个 I/O 请求;

✅ 支持几乎所有 I/O 类型:文件、socket、pipe、eventfd 等;除了读写操作外,还包括打开文件(open),获取文件信息(stat)、关闭文件(close)和删除文件(unlink)等操作。

Linux 内核 5.10以后,io_uring 才基本成熟稳定,2023 年一批著名的开源软件如 redis、nginx 等适配了 io_uring。 Linux内核版本 >= 6.2 时 io_uring功能更齐全,推荐内核版本至少为 6.2。io_uring 的原理与 RDMA 的 verbs接口类似,二者都是采用请求队列和完成队列的方式。io_uring 完全可以取代传统的 epoll,在高并发场景下有明显性能优势。io_uring 的使用门槛比epoll 略高,比RDMA verbs 明显要低。 RDMA verbs 和 epoll 可以完美配合,但和 io_uring配合有些问题,并且从理论上讲并没有性能上的优势,因此我果断放弃 io_uring 对 RDMA verbs 的整合。

在我适配 io_uring 的过程中,一些不确定的地方经常询问大模型,大模型给出的回答很给力,大大提高了开发效率。在大模型的帮助下,我用了大约两周时间完成 FastDFS 和 FastCFS 的 io_uring适配。我在使用过程中发现大模型难免存在幻觉问题,在缺乏业界经验数据支持的情况下,大模型根据自己的推理机制一本正经给出错误答案,特别容易误导人。我把使用 io_uring 经验整理出来,其中若干条是大模型搞不清楚或者弄混淆了的,希望对大家有所帮助。

io_uring 异步IO框架的核心系统调用主要包括io_uring_setup、io_uring_register 和 io_uring_enter,使用门槛较高。我们可以使用 liburing 封装,这是业界主流做法。

安装 liburing 开发包:

CentOS、Rocky Linux、AlmaLinux 和 RHEL 等 Linux 发行版:

sudo yum install liburing-devel -y

Debian 和 Ubuntu 等 Linux 发行版:

sudo apt install liburing-dev -y

像 Rocky Linux 之类的 Linux 发行版,默认是禁用 io_uring 的,需要采用如下命令行开启(注意:服务器重启后将失效):

sysctl -w kernel.io_uring_disabled=0

也可以在 /etc/sysctl.conf 或专用配置文件(如 /etc/sysctl.d/99-io_uring.conf)中加入如下配置以永久生效:

kernel.io_uring_disabled=0

io_uring demo 或测试用途的server和client 根据大模型生成并做了修改,获取命令行如下:

curl -o io_uring_echo_server.c http://www.fastken.cn/test/io_uring_echo_server.ccurl -o io_uring_echo_client.c http://www.fastken.cn/test/io_uring_echo_client.c

编译命令如下:

gcc -Wall -O2 -o io_uring_echo_server io_uring_echo_server.c -luringgcc -Wall -O2 -o io_uring_echo_client io_uring_echo_client.c -luring

io_uring 相关封装在基础库 libfastcommon 的 ioevent.[hc]和 ioevent_loop.[hc]中,以及网络框架库libserverframe 的 sf_nio.c 中,供感兴趣的朋友参考。

io_uring 是异步 IO 机制,IO 操作提交到请求队列,IO完成后会通过完成队列得到cqe,结果存放在 cqe->res 中,比如读取或写入的字节数、建立连接后得到的 socket fd 等,负数表示错误(负的错误码)。

因为 io_uring 和 RDMA verbs 均为纯异步 IO方式,IO 操作提交给 io_uring / verbs 后尚未完成前,对应的buffer 需要保持有效(不能被释放或被复用),推荐采用引用计数的方式来保证 buffer 的有效性。

io_uring_prep_poll_multishot 通常用于监听 fd 读写事件, 只需调用一次即可多次触发事件通知,在连接关闭等情况下不再需要事件通知时,需要显式调用 io_uring_prep_cancel 取消,此时至少会收到两条通知,一条为cancel 操作的完成通知(通常cqe->res 为 1,表示成功取消了一个 IO 请求/操作),而另一条则为poll_multishot操作的完成通知(cqe->res 为 -ECANCELED,表示该请求已被取消)。

io_uring_prep_xxx 函数会将seq->flags 设置为 0,因此一定要在io_uring_prep_xxx 后设置 seq->flags,比如:

struct io_uring_sqe *sqe = io_uring_get_sqe(&ioevent->ring);
if (sqe == NULL) {return ENOSPC;
}io_uring_prep_cancel_fd(sqe, fd, 0);
/* set sqe->flags MUST after io_uring_prep_xxx */
sqe->flags = IOSQE_CQE_SKIP_SUCCESS;

提升文件读写性能,可以使用 io_uring_prep_read_fixed 和 io_uring_prep_write_fixed,使用到的 buffer 需要事先注册。有两种注册方式:一次性注册和按需注册。一次性注册 API 为io_uring_register_buffers 和 io_uring_register_buffers_tags,使用其中之一即可;按需注册先使用 io_uring_register_buffers_sparse 占位,然后使用 io_uring_register_buffers_update_tag按需更新一个或多个buffer。注意:对于同一个 ring,注册 buffer 的 API 只能调用一次(除非取消注册后重新注册),而不是大模型回答的可以调用多次。io_uring_prep_read_fixed 和 io_uring_prep_write_fixed的最后一个参数 buf_index 使用的是基于0的 buffer 索引号。

友情提示

由于内核限制,io_uring_queue_init 的 entries 上限为32K,io_uring_register_buffers 和 io_uring_register_buffers_tags注册的 buffer 数量上限为 16K;

在采用共享buffer 池(即内存池)下,一个 buffer 可以注册到多个 ring;

io_uring_register_buffers_update_tag 在修改 buffer 的情况下,将收到完成通知, 此时cqe->user_data 为原有tag 值(即旧tag的值)。一次性调用 io_uring_register_buffers_sparse 占位,然后调用io_uring_register_buffers_update_tag 首次设置其中的 buffer 不会触发通知。如果 buffer 数超过了注册上限,则回退为 io_uring_prep_read 和 io_uring_prep_write;

io_uring 的网络接收和发送数据的行为模式与传统的 recv 和 send 完全一致,比如接收较大的数据包(如 64KB),通常需要多次调用 recv 才可以完成整个数据包的接收。

尝试以零拷贝方式发送数据的函数为io_uring_prep_send_zc,目前的虚机和大多数网卡都支持内核的零拷贝发送方式。调用io_uring_prep_send_zc 后会收到两条通知:一条为发送情况通知(发送的字节数,0 字节表示发送失败);另外一条为发送完成通知,对应的cqe->flags会有 IORING_CQE_F_NOTIF标记(即 (cqe->flags & IORING_CQE_F_NOTIF) != 0),收到发送完成通知后方可释放或复用buffer。

io_uring_prep_send_zc 函数的最后一个参数传递 IORING_SEND_ZC_REPORT_USAGE,表示需要报告是否实现了零拷贝。当接收到发送完成通知时,可以通过 cqe->res & IORING_NOTIF_USAGE_ZC_COPIED 判断零拷贝情况,(cqe->res & IORING_NOTIF_USAGE_ZC_COPIED) == 0 表示实现了零拷贝。经过实测,同一台虚机内通信不会零拷贝,跨服务器通信才有可能零拷贝。

最后总结一下使用io_uring 的高性能做法:

  • 多次io_uring_prep_xxx 后,调用一次 io_uring_submit 实现批量提交;
  • io_uring_wait_cqe 或 io_uring_wait_cqe_timeout 成功后,调用 io_uring_for_each_cqe 遍历所有 cqe;
  • 如果网卡支持内核的零拷贝机制,尽量使用io_uring_prep_send_zc发送数据,否则直接使用io_uring_prep_send。
http://www.dtcms.com/a/592999.html

相关文章:

  • (附源码)基于Spring boot的校园志愿服务管理系统的设计与实现
  • deepseek回答 如何用deepseek训练出一个我的思路
  • 3ds Max材质高清参数设置:10分钟提升渲染真实感
  • MyBatis 插件
  • 甘肃省城乡住房建设厅网站首页微商软件自助商城
  • 一文掌握,kanass安装与配置
  • C# ASP.NET MVC 数据验证实战:View 层双保险(Html.ValidationMessageFor + jQuery Validate)
  • 工信部 网站 邮箱内容管理系统做网站
  • arcgis用累计值进行分级
  • 生理学实验系统 生理学实验系统软件 集成化生物信号采集与处理系统生物信号采集处理系统 生理机能实验处理系统
  • 环境变量与程序地址空间
  • Node.js的主要应用场景和简单例子
  • 做视频解析网站是犯法的么360优化大师
  • 大网站cn域名淘宝店铺装修模板免费下载
  • VBA即用型代码手册:利用函数保存为PDF文件UseFunctionSaveAsPDF
  • JPA 的说明和使用
  • MyBatis使用LocalDateTime会报错
  • web网页开发,在线财务管理系统,基于Idea,html,css,jQuery,java,ssm,mysql。
  • 2025汉化idea创建JSP项目
  • 如何高效处理日常 PDF 文档?
  • LeetCode 2342.数位和相等数对的最大和
  • 企业网站建设需了解什么软文投放平台有哪些?
  • pink老师html5+css3day07
  • 各个手机芯片型号
  • [Qt学习笔记]Qt5.15.2版本安装及调整组件
  • C语言--文件读写函数的使用,对文件读写知识有了更深的了解。
  • WEBweb前端OPPO手机商城网站项目
  • 虚拟技术 云手机是指什么?
  • 传播易网站开发方案饿了么网站做生鲜吗
  • 制作网站的网站fullpage网站怎么做