linux 线程 图解原理|Linux I/O 神器之 io_uring
鉴于原生 AIO 存
io_uring 是 Linux 于 2019 年加入到内核的一种新型异步 I/O 模型,io_uring 主要为了解决 原生AIO(Native AIO) 存在的一些不足之处。下面介绍一下原生 AIO 的不足之处: 鉴于原生 AIO 存在这么多不足之处,于是乎 Jens Axboe(io_uring 作者)就开发出一套全新的异步 I/O 接口来解决这些问题。 既然 io_uring 这么优秀,我们就来学习一下其先进思想吧!下面将会介绍 io_uring 的原理。io_uring 的出现就是为了解决上面的问题,我们来看看 io_uring 是怎么处理的。 1. 减少系统调用 由于调用系统调用时,会从用户态切换到内核态,从而进行上下文切换,而上下文切换会消耗一定的 CPU 时间。 使用 read() 和 write() 等系统调用进行 I/O 操作时,会从用户态嵌入到内核态,如下图所示: io_uring 为了减少或者摒弃系统调用,采用了用户态与内核态 共享内存 的方式来通信。如下图所示: 用户进程可以向 共享内存 提交要发起的 I/O 操作,而内核线程可以从 共享内存 中读取 I/O 操作,并且进行相关的 I/O 操作。 用户态对共享内存进行读写操作是不需要使用系统调用的,所以不会发生上下文切换的情况。 相关视频推荐 6种epoll的设计,让你吊打面试官,而且他不能还嘴 epoll原理剖析以及三握四挥的处理 LinuxC++后台服务器开发架构师免费学习地址 LinuxC++后台开发学习路线: Linux C/C++后端服务器架构开发 成长体系 【文章福利】:小编整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!~点击832218493加入(需要自取) 2. 提交队列与完成队列 前面介绍过,io_uring 通过用户态与内核态共享内存的方式,来免去了使用系统调用发起 I/O 操作的过程。 io_uring 主要创建了 3 块共享内存: 它们之间的关系如下图所示: 提交队列 在内核中,使用 io_sq_ring 结构来表示 提交队列,其定义如下:
io_sq_ring 结构各个字段的含义如下: io_sq_ring 的结构图如下所示: 内核会将 io_sq_ring 结构映射到应用程序的内存空间,这样应用程序与内核都能操作 io_sq_ring 结构。应用程序可以直接向 io_sq_ring 结构的环形队列中提交 I/O 操作,而不用通过系统调用来提交,从而避免了上下文切换的发生。 而内核线程可以通过从 io_sq_ring 结构的环形队列中获取到要进行的 I/O 操作,并且发起 I/O 请求。 提交队列项 从上面的分析可知,io_sq_ring 结构 array 字段只是一个整形类型的数组,用于存储指向 提交队列项数组 的的索引。在内核中,提交队列项 使用 io_uring_sqe 结构表示,其定义如下:
下面介绍一下 io_uring_sqe 结构各个字段的作用: 当用户调用 io_uring_setup() 系统调用创建一个 io_ring 对象时,内核将会创建一个类型为 io_uring_sqe 结构的数组。内核也会将此数组映射到应用程序的内存空间,这样应用程序就可以直接操作这个数组。 应用程序提交 I/O 操作时,先要从 提交队列项数组 中获取一个空闲的项,然后向此项填充数据(如 I/O 操作码、要进行 I/O 操作的文件句柄等),然后将此项在 提交队列项数组 的索引写入 提交队列 中。 liburing 代码库已经把这些繁琐的操作封装成友好的 API,用户只需要直接调用这些 API 来进行操作即可。 关于 liburing 代码库的使用,可以参考其使用手册,本文不作详细介绍。完成队列 当内核完成 I/O 操作后,会将 I/O 操作的结果保存到 完成队列 中。内核使用 io_cq_ring 结构来表示,其定义如下:
完成队列 与 提交队列 类似,也是一个环形队列。下面介绍一下 io_cq_ring 结构各个字段的作用: io_cq_ring 的结构图如下所示: 内核也会将 完成队列 映射到应用程序的内存空间,这样应用程序就可以通过读取完成队列来获取 I/O 操作的结果。而不用通过使用系统调用来获取,从而避免了不必要的上下文切换。 3. SQ 线程 前面介绍了 io_uring 怎么通过共享 提交队列 和 完成队列 来避免不必要的系统调用linux 线程,但应用程序将 I/O 操作提交到 提交队列 后,内核什么时候从 提交队列 中获取要进行的 I/O 操作,并且发起 I/O 请求呢? 当用户使用 SQPOLL 模式(指定了 IORING_SETUP_SQPOLL 标志)创建 io_uring 时,内核将会创建一个名为 io_uring-sq 的内核线程(称为 SQ 线程),此内核线程会不断从 提交队列 中读取 I/O 操作,并且发起 I/O 请求。 当 I/O 请求完成以后,SQ 线程将会把 I/O 操作的结果写入到 完成队列 中,应用程序就可以从 完成队列 中读取 I/O 操作的结果。 如下图所示: 我们简单总结下 io_uring 的操作步骤: 4. 总结 io_uring 主要通过用户态与内核态共享内存的途径,来摒弃使用系统调用来提交 I/O 操作和获取 I/O 操作的结果,从而避免了上下文切换的情况。另外,由于用户态进程与内核态线程通过共享内存的方式通信,从而避免了内存拷贝的过程,提升了 I/O 操作的性能。 所以,io_uring 主要通过两个优化点来提升 I/O 操作的性能: 原文:图解原理|Linux I/O 神器之 io_uring 侵删~ (编辑:91站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |