一篇文章带你读懂 io_uring 的接口与实现

作者&投稿:泊虽 (若有异议请与网页底部的电邮联系)
~

io_uring 是 Linux 提供的一个异步 I/O 接口。io_uring 在 2019 年加入 Linux 内核,经过了两年的发展,现在已经变得非常强大。本文基于 Linux 5.12.10 介绍 io_uring 接口。

io_uring 的实现主要在 fs/io_uring.c 中。

io_uring 的实现仅仅使用了三个 syscall:io_uring_setup, io_uring_enter 和 io_uring_register。它们分别用于设置 io_uring 上下文,提交并获取完成任务,以及注册内核用户共享的缓冲区。使用前两个 syscall 已经足够使用 io_uring 接口了。

用户和内核通过提交队列和完成队列进行任务的提交和收割。后文中会出现大量的简写,在这里先做一些介绍。

用户通过调用 io_uring_setup 1 初始化一个新的 io_uring 上下文。该函数返回一个 file descriptor,并将 io_uring 支持的功能、以及各个数据结构在 fd 中的偏移量存入 params。用户根据偏移量将 fd 映射到内存 (mmap) 后即可获得一块内核用户共享的内存区域。这块内存区域中,有 io_uring 的上下文信息:提交队列信息 (SQ_RING) 和完成队列信息 (CQ_RING);还有一块专门用来存放提交队列元素的区域 (SQEs)。SQ_RING 中只存储 SQE 在 SQEs 区域中的序号,CQ_RING 存储完整的任务完成数据。2

在 Linux 5.12 中,SQE 大小为 64B,CQE 大小为 16B。因此,相同数量的 SQE 和 CQE 所需要的空间不一样。初始化 io_uring 时,用户如果不在 params 中设置 CQ 长度,内核会分配 entries 个 SQE,以及 entries * 2 个 CQE。

io_uring_setup 设计的巧妙之处在于,内核通过一块和用户共享的内存区域进行消息的传递。在创建上下文后,任务提交、任务收割等操作都通过这块共享的内存区域进行,在 IO_SQPOLL 模式下(后文将详细介绍),可以完全绕过 Linux 的 syscall 机制完成需要内核介入的操作(比如读写文件),大大减少了 syscall 切换上下文、刷 TLB 的开销。

io_uring 可以处理多种 I/O 相关的请求。比如:

下面以 fsync 为例,介绍执行这个操作中可能用到的结构体和函数。

io_op_def io_op_defs[] 数组中定义了 io_uring 支持的操作,以及它在 io_uring 中的一些参数。3 比如 IORING_OP_FSYNC:

io_uring 中几乎每个操作都有对应的准备和执行函数。比如 fsync 操作就对应 io_fsync_prep 和 io_fsync函数。

除了 fsync 这种同步(阻塞)操作,内核中还支持一些异步(非阻塞)调用的操作,比如 Direct I/O 模式下的文件读写。对于这些操作,io_uring 中还会有一个对应的异步准备函数,以 _async 结尾。比如:

这些函数就是 io_uring 对某个 I/O 操作的包装。

用户将需要进行的操作写入 io_uring 的 SQ 中。在 CQ 中,用户可以收割任务的完成情况。这里,我们介绍 SQE 和 CQE 的编码。

include/uapi/linux/io_uring.h 4 中定义了 SQE 和 CQE。SQE 是一个 64B 大小的结构体,里面包含了所有操作可能用到的信息。

io_uring_sqe的定义

CQE 是一个 16B 大小的结构体,包含操作的执行结果。

继续以 fsync 为例。要在 io_uring 中完成 fsync 操作,用户需要将 SQE 中的 opcode 设置为 IORING_OP_FSYNC,将 fd 设置为需要同步的文件,并填充 fsync_flags。其他操作也是类似,设置 opcode 并将操作所需要的参数并写入 SQE 即可。

通常来说,使用 io_uring 的程序都需要用到 64 位的 user_data 来唯一标识一个操作 5。user_data 是 SQE 的一部分。io_uring 执行完某个操作后,会将这个操作的 user_data 和操作的返回值一起写入 CQ 中。

相关视频推荐

io_uring 新起之秀的linux io模式,是如何媲美epoll的

网络原理tcp/udp,网络编程epoll/reactor,面试中正经“八股文”

学习地址:https://ke.qq.com/course/417774?flowToken=1013300

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括 C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等),免费分享

io_uring 通过环形队列和用户交互。

我们的先以用户提交任务为例,介绍 io_uring 的内核用户交互方式。用户提交任务的过程如下:

接下来我们简要介绍内核获取任务、内核完成任务、用户收割任务的过程。

介绍完 io_uring 的用户态接口后,我们就可以详细介绍 io_uring 在内核中是如何实现的了。

io_uring 在创建时有两个选项,对应着 io_uring 处理任务的不同方式:

这些选项的设定会影响之后用户与 io_uring 交互的方式:

每个 io_uring 都由一个轻量级的 io-wq6 线程池支持,从而实现 Buffered I/O 的异步执行。对于 Buffered I/O 来说,文件的内容可能在 page cache 里,也可能需要从盘上读取。如果文件的内容已经在 page cache 中,这些内容可以直接在 io_uring_enter 的时候读取到,并在返回用户态时收割。否则,读写操作会在 workqueue 里执行。

如果没有在创建 io_uring 时指定 IORING_SETUP_IOPOLL 选项,io_uring 的操作就会放进 io-wq 中执行。

上图覆盖了关闭 IOPOLL 模式下,用户通过 io_uring 执行操作的整个调用流程。用户提交的 SQE 经过一系列处理后,会在 io_queue_sqe 中试探着执行一次。

所有的操作都被提交到内核队列后,如果用户设置了 IORING_ENTER_GETEVENTS flag,io_uring_enter 在返回用户态前会等待指定个数的操作完成。

之后,Linux 随时会调度 io-wq 的内核线程执行。此时,io_wq_submit_work 函数会不断用阻塞模式执行用户指定的操作。某个操作完整执行后,它的返回值就会被写入 CQ 中。用户通过 io_uring 上下文中的 CQ 队尾位置就能知道内核处理好了哪些操作,无需再次调用 io_uring_enter。

通过火焰图可以观察到,在关闭 IOPOLL 时,内核会花大量时间处理读取操作。

创建 io_uring 时指定 IORING_SETUP_IOPOLL 选项即可开启 I/O 轮询模式。通常来说,用 O_DIRECT 模式打开的文件支持使用轮询模式读写内容,执行 read / write 操作。

在轮询模式下,io_uring_enter 只负责把操作提交到内核的文件读写队列中。之后,用户需要多次调用 io_uring_enter 来轮询操作是否完成。

在轮询模式下,io-wq 不会被使用。提交任务时,io_read 直接调用内核的 Direct I/O 接口向设备队列提交任务。

如果用户设置了 IORING_ENTER_GETEVENTS flag,在返回用户态前,io_uring_enter 会通过 io_iopoll_check 调用内核接口轮询任务是否完成。

通过火焰图可以看到,io_uring_enter 在提交任务这一块只花了一小部分时间。大部分时间都在轮询 I/O 操作是否完成。

在实际生产环境中,我们往往会有这样的需求:往文件中写入 n 次,然后用 fsync 落盘。在使用 io_uring时,SQ 中的任务不一定会按顺序执行。给操作设定 IO_SQE_LINK 选项,就可以建立任务之间的先后关系。IO_SQE_LINK 之后的第一个任务一定在当前任务完成后执行。7

io_uring 内部使用链表来管理任务的依赖关系。每一个操作在经过 io_submit_sqe 的处理后,都会变成一个 io_kiocb 对象。这个对象有可能会被放入链表中。io_submit_sqe 8 会对含有 IO_SQE_LINK 的 SQE 作特殊处理,处理过程如下:

由此看来,SQ 中连续的 IO_SQE_LINK 记录会按先后关系依次处理。在 io_submit_sqes 结束前,所有的任务都会被提交。因此,如果任务有先后关系,它们必须在同一个 io_uring_enter syscall 中批量提交。

其他用于控制 io_uring 任务依赖的选项包括 IOSQE_IO_DRAIN 和 IOSQE_IO_HARDLINK,这里不再展开。




峨山彝族自治县15636037817: 求一篇关于internet的英文文章短一点的 -
沙柔核黄: The Internet was the result of some visionary thinking by people in the early 1960s who saw great potential value in allowing computers to share information on research and development in scientific and military fields. J.C.R. Licklider of MIT, first ...

峨山彝族自治县15636037817: 请帮我找一篇关于Internet的英语文章!
沙柔核黄: Internet is more and more widely used in our daily life.Many people like to work on the computers.Many compains and more and more school have got computers.You can find many information on the internet if you can not go out.You can search the...

峨山彝族自治县15636037817: 一篇文章读懂人身保险,怎么购买人身保险 -
沙柔核黄: 我们都知道,人身保险是关于人体本身的、人的健康、人的生命的保险.当人们遭受不幸事故或因疾病丢失工作能力、残疾、死亡或年老退休,根据保险合同约定,保险公司支付保险或年金到被保险人或受益人,解决病、残、老、死的经济困难...

峨山彝族自治县15636037817: 求一篇与INTERNET 有关的英文文章
沙柔核黄: 我有一篇啊,呵呵~不过打上来太慢了... The Internet In my opinion ,the internet is helpful rather than harmful as someone else thinks .As is known to all ,the internet is playing a more and more important part in our daily life . On the internet ,we can ...

峨山彝族自治县15636037817: 怎么能读懂一篇文章 -
沙柔核黄: 如果比较难弄的文章的话,对于其中的情节可以假设是发生在自己的身上,联想自身的心情,将作者当时的心情大概的比拟对起来,这样的话,就可以理解作者写文章的时候的心情、心境.如果有必要的话也可以查一下这篇文章的作者的写作背景,比如像鲁迅的文章,如果你不知道他的写作背景,那么就很难看懂到底想表达什么了.欣赏或者说读懂文章并非将这些字都理解,最重要的就是心与心的交流.作者写出来东西就是希望交流与分享.以上是个人的体会.

峨山彝族自治县15636037817: 怎样才能读懂一篇英语文章? -
沙柔核黄: 我认为:首先你要对英语有感觉,这感觉来自你对英语的兴趣大小.其次需要解决的就是单词量,这个是必须的,也是十分重要的,文章就是由单词按一定语法写出来的,说白了读文章就是读单词.两项都具备了,你需要在多读一些读物,刚开始可以读简单易懂的,慢慢地可以加深难度.一方面锻炼你的阅读量,还可以增加单词量,也可以巩固以前记过的单词.还有就是要坚持,天道酬勤,努力才能做到.

峨山彝族自治县15636037817: 我要一篇关于tree的文章,最好是英文,字数在100个 -
沙柔核黄: Tree As everybody knows,wood has many uses.It is a kind of very important building material.Wood can be made into desks,chairs,beds and many other things.Fine paper is made of wood,too.Only trees can provide us with enough wood.Trees can...

峨山彝族自治县15636037817: java 如何计算一篇文章有多少字符 速度速度!!! -
沙柔核黄: 只计算长度的话,没必要保存内容,直接相加每一行内容的长度即可.这个只有一个int变量和一个string临时变量应该是最节省内存的了.import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class FileCount ...

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网