Linux I/O神器之io_uring

io_uring 是 Linux 于 2019 年加入到内核的一种新型异步 I/O 模型,io_uring 主要为了解决 原生AIO(Native AIO) 存在的一些不足之处。下面介绍一下原生 AIO 的不足之处:

  • 系统调用开销大:提交 I/O 操作和获取 I/O 操作的结果都需要通过系统调用完成,而触发系统调用时,需求进行上下文切换。在高 IOPS(Input/Output Per Second)的情况下,进行上下文切换也会消耗大量的CPU时间。
  • 仅支持 Direct I/O(直接I/O):在使用原生 AIO 的时候,只能指定 O_DIRECT 标识位(直接 I/O),不能借助文件系统的页缓存(page cache)来缓存当前的 I/O 请求。
  • 对数据有大小对齐限制:所有写操作的数据大小必须是文件系统块大小(一般为4KB)的倍数,而且要与内存页大小对齐。
  • 数据拷贝开销大:每个 I/O 提交需要拷贝 64+8 字节,每个 I/O 完成结果需要拷贝 32 字节,总共 104 字节的拷贝。这个拷贝开销是否可以承受,和单次 I/O 大小有关:如果需要发送的 I/O 本身就很大,相较之下,这点消耗可以忽略。而在大量小 I/O 的场景下,这样的拷贝影响比较大。

鉴于原生 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 操作。

用户态对共享内存进行读写操作是不需要使用系统调用的,所以不会发生上下文切换的情况。

2. 提交队列与完成队列

前面介绍过,io_uring 通过用户态与内核态共享内存的方式,来免去了使用系统调用发起 I/O 操作的过程。

io_uring 主要创建了 3 块共享内存:

  • 提交队列(Submission Queue, SQ):一整块连续的内存空间存储的环形队列,用于存放将执行 I/O 操作的数据(指向提交队列项数组的索引)。
  • 完成队列(Completion Queue, CQ):一整块连续的内存空间存储的环形队列,用于存放 I/O 操作完成后返回的结果。
  • 提交队列项数组(Submission Queue Entry,SQE):提交队列中的一项。

它们之间的关系如下图所示:

提交队列

在内核中,使用 io_sq_ring 结构来表示 提交队列,其定义如下:

struct io_sq_ring {
    struct io_uring {
        u32 head;
        u32 tail;
    }                   r;             // 使用head和tail指针来模拟环形操作
    ...
    u32                 ring_entries;  // 队列中的提交项总数
    ...
    u32                 flags;
    u32                 array[];       // 环形队列数组(指向提交队列项数组的索引)
};

io_sq_ring 结构各个字段的含义如下:

  • head:环形队列的头指针。
  • tail:环形队列的尾指针。
  • ring_entries:队列中已存在的 I/O 操作项总数。
  • array:环形队列数组,指向提交队列项数组的索引。

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 结构表示,其定义如下:

struct io_uring_sqe {
    __u8    opcode;     /* type of operation for this sqe */
    ...
    __u16   ioprio;     /* ioprio for the request */
    __s32   fd;         /* file descriptor to do IO on */
    __u64   off;        /* offset into file */
    __u64   addr;       /* pointer to buffer or iovecs */
    __u32   len;        /* buffer size or number of iovecs */
    ...
};

下面介绍一下 io_uring_sqe 结构各个字段的作用:

  • opcode:I/O 操作码,主要用于表示当前的 I/O 操作是什么类型,如读、写或者同步等。
  • ioprio:I/O 操作的优先级,可以通过此字段来把一些重要的 I/O 操作提前执行。
  • fd:I/O 操作对应的文件句柄。
  • off:当前 I/O 操作的偏移量。
  • addr:用于指向当前 I/O 操作所关联的内存地址。如写操作,指向的是要写入到文件的内容的内存地址。
  • len:表示当前 I/O 操作的数据长度。

当用户调用 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 结构来表示,其定义如下:

struct io_cq_ring {
    struct io_uring {
        u32 head;
        u32 tail;
    };
    ...
    u32                 ring_entries;
    ...
    struct io_uring_cqe cqes[];
};

struct io_uring_cqe {
    __u64   user_data;  // 指向 I/O 操作返回的数据
    __s32   res;        // I/O 操作的结果
    ...
};

完成队列 与 提交队列 类似,也是一个环形队列。下面介绍一下 io_cq_ring 结构各个字段的作用:

  • head:环形队列的头指针。
  • tail:环形队列的尾指针。
  • ring_entries:已完成的 I/O 操作总数。
  • cqes:用于保存 I/O 操作结果的环形队列数组,其元素类型为 io_uring_cqe 结构。

io_cq_ring 的结构图如下所示:

内核也会将 完成队列 映射到应用程序的内存空间,这样应用程序就可以通过读取完成队列来获取 I/O 操作的结果。而不用通过使用系统调用来获取,从而避免了不必要的上下文切换。

【提交队列、完成队列,均为1v1无锁队列!】

3. SQ 线程

前面介绍了 io_uring 怎么通过共享 提交队列 和 完成队列 来避免不必要的系统调用,但应用程序将 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 操作的结果。

【SQ线程,r2c模式,完成前后端收割!】

如下图所示:

我们简单总结下 io_uring 的操作步骤:

  • 第一步:应用程序通过向 io_uring 的 提交队列 提交 I/O 操作。
  • 第二步:SQ内核线程从 提交队列 中读取 I/O 操作。
  • 第三步:SQ内核线程发起 I/O 请求。
  • 第四步:I/O 请求完成后,SQ内核线程会将 I/O 请求的结果写入到 io_uring 的 完成队列 中。
  • 第五步:应用程序可以通过从 完成队列 中读取到 I/O 操作的结果。

4. 总结

io_uring 主要通过用户态与内核态共享内存的途径,来摒弃使用系统调用来提交 I/O 操作和获取 I/O 操作的结果,从而避免了上下文切换的情况。另外,由于用户态进程与内核态线程通过共享内存的方式通信,从而避免了内存拷贝的过程,提升了 I/O 操作的性能。

所以,io_uring 主要通过两个优化点来提升 I/O 操作的性能:

  • 摒弃使用系统调用来提交 I/O 操作和获取 I/O 操作结果。
  • 减少用户态与内核态之间的内存拷贝。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/257536.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity中 URP 下的棋盘格Shader

文章目录 前言一、制作思路法1&#xff1a;使用纹理采样后&#xff0c;修改重铺效果法2&#xff1a;计算实现 二、粗略计算实现棋盘格效果1、使 uv.x < 0.5 区域 0 。反之&#xff0c; 0.52、使 uv.y < 0.5 区域 0 。反之&#xff0c; 0.53、使两个颜色相加4、取小数…

Selenium Wire - 扩展 Selenium 能够检查浏览器发出的请求和响应

使用 Selenium 进行自动化操作时&#xff0c;会存在很多的特殊场景&#xff0c;比如会修改请求参数、响应参数等。 本篇将介绍一款 Selenium 的扩展&#xff0c;即能够检查浏览器发出的请求和响应 - Selenium Wire。 简介 Selenium Wire 扩展了 Selenium 的 Python 绑定&…

修复泰坦陨落2缺少msvcr120.dll的5种方法,亲测有效

游戏《泰坦陨落2》缺少msvcr120.dll的问题困扰着许多玩家。这个问题的主要原因可能是系统环境不完整、软件或游戏版本不匹配、DLL文件丢失或损坏以及杀毒软件误判等。msvcr120.dll是Microsoft Visual C 2013 Redistributable的一个组件&#xff0c;它包含了许多运行库文件&…

JavaScript中的await-async-事件循环-异常处理

一、async、await 1.异步函数 async function async关键字用于声明一个异步函数&#xff1a; async是asynchronous单词的缩写&#xff0c;异步、非同步&#xff1b; sync是synchronous单词的缩写&#xff0c;同步、同时&#xff1b; async异步函数可以有很多中写法&#x…

Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 堆的说明 2.0 堆的成员变量及其构造方法 3.0 实现堆的核心方法 3.1 实现堆的核心方法 - 获取堆顶元素 peek() 3.2 实现堆的核心方法 - 下潜 down(int i) 3.3 实…

关键点检测_labelme标注的json,随机裁剪(添加偏移相当于数据增强)

import json import os import numpy as np import cv2 import glob import csv import random#通过表格获取csv # def csv_tws(root, name): # csv_path = root+"/csv/{}.png.csv".format(name)

Java并发(二十)----synchronized原理进阶

1、小故事 故事角色 老王 - JVM 小南 - 线程 小女 - 线程 房间 - 对象 房间门上 - 防盗锁 - Monitor-重量级锁 房间门上 - 小南书包 - 轻量级锁 房间门上 - 刻上小南大名 - 偏向锁 -对象专属于某个线程使用 批量重刻名 - 一个类的偏向锁撤销到达 20 阈值 -批量重偏向 …

linux之Samba服务器

环境&#xff1a;虚拟机CENTOS 7和 测试机相通 一、Samba服务器_光盘共享&#xff08;匿名访问&#xff09; 1.在虚拟机CENTOS 7安装smb服务&#xff0c;并在防火墙上允许samba流量通过 2. 挂载光盘 3.修改smb.conf配置文件&#xff0c;实现光盘匿名共享 4. 启动smb服务 5.在…

Postman使用总结--生成测试报告

1.执行生成的命令格式 newman run 用例集文件 .json -e 环境文件 .json -d 数据文件 .json/.csv -r htmlextra --reporter- htmlextra-export 测试报告名 .html -e 和 -d 是 非必须的。 如果没有使用 环境&#xff0c;不需要指定 -e 如果没有使用 数据…

二叉树【数据结构】

目录 二叉树1. 二叉树定义二叉树的存储定义 2. 遍历二叉树(1) 前序遍历(2) 中序遍历(3) 后序遍历(4) 层序遍历 3. 二叉树的相关操作(1) 二叉树的初始化(2) 二叉树的结点的手动创建(3) 二叉树结点的个数(4) 二叉树叶子结点的个数(5) 二叉树的高度(6) 第k层结点个数(7) 通过前序遍…

机场信息集成系统系列介绍(4):机场信息集成总线系统

机场信息集成总线系统是一种专为机场运营管理设计的先进系统&#xff0c;旨在提高机场的航班调度指挥效率&#xff0c;同时为机场各生产部门提供航班保障计划的制定和实时调整功能。该系统的核心用户是机场运控部门。 机场信息集成总线系统&#xff08;Airport Information In…

Qt-QTransform介绍与使用

QTransform是一个用于二维坐标系转换的类。我们知道Qt的坐标系是左上角为原点&#xff0c;x轴向右&#xff0c;y轴向下&#xff0c;屏幕上每个像素代表一个单位&#xff0c;那么&#xff0c;如果我们想要在屏幕上建立自己的坐标系用于绘制&#xff0c;就需要借助QTransform。 …

什么品牌的猫罐头好吃?五大性价比高的猫罐头测评

不知不觉已经养猫两年啦&#xff0c;大大小小也算是尝试过很多猫罐头了。一开始我也是踩了很多坑&#xff0c;各种踩雷。我深知猫罐头的各种门道&#xff0c;新手一不小心就会着道了。 作为一个经营猫咖5年的老板&#xff0c;大促期间我总能捡漏&#xff0c;屯到一大波好吃又放…

一、Java基础语法

注意&#xff1a; ​ 用记事本打开本文档&#xff0c;格式较差。 ​ 可安装typora软件后再次打开。 ​ 安装包位于&#xff1a;day01\资料\其他软件\阅读笔记的软件\typora-setup-x64.exe day01 - Java基础语法 1. 人机交互 1.1 什么是cmd&#xff1f; 就是在windows操作…

JS的浅拷贝和深拷贝

首先理解什么是浅拷贝和深拷贝&#xff1a; 浅拷贝&#xff1a; 浅拷贝只会复制对象的第一层属性&#xff0c;而不会递归地复制嵌套的对象。浅拷贝仅复制对象的引用&#xff0c;新对象和原始对象仍然共享相同的引用&#xff0c;因此对新对象的修改可能会影响到原始对象。浅拷…

Postman/Apifox使用教程

Postman/Apifox使用教程 1. 界面导航说明2.发送第一个请求3. 工具的基础功能3.1 常见类型的接口请求3.1.1 查询参数的接口请求3.1.2 表单类型的接口请求3.1.3 上传文件的表单请求3.1.4 json类型的接口请求 3.2 接口响应数据解析 附录 1. 界面导航说明 2.发送第一个请求 http:/…

css的filter全属性介绍

原图&#xff1a; 模糊&#xff08;blur&#xff09; 单位可为px或rem&#xff0c;值越大&#xff0c;越模糊 filter:blur(3px) filter:blur(0.3rem) 亮度(brightness) 值可为数字或百分数&#xff0c;小于1时&#xff0c;亮度更暗&#xff1b;等于1时&#xff0c;无变化&am…

非递归实现的快速排序

目录 序列文章 前言 学前补充 非递归快速排序 注意事项&#xff08;重要&#xff09; 实现步骤 代码实现 时空复杂度 快速排序的特性 栈的相关代码 序列文章 非递归实现的快速排序&#xff1a;http://t.csdnimg.cn/UEcL6 快速排序的挖坑法与双指针法&#xff1a;ht…

如何免费搭建私人电影网站(二)

前一篇的准备工作做好后就进行下面的具体操作 1、免费主机申请步骤 产品——立即开通 开通成功后会出现IP地址和网站地址如下图 点击进去管理 设置FTP密码和MYSQL数据库密码 设置好后&#xff0c;就可以通过FTP文件上传工具&#xff0c;将下载好的网站模版上传到空间了 FTP…

178. 第K短路(A*启发式算法)

178. 第K短路 - AcWing题库 给定一张 N 个点&#xff08;编号 1,2…N&#xff09;&#xff0c;M 条边的有向图&#xff0c;求从起点 S 到终点 T 的第 K 短路的长度&#xff0c;路径允许重复经过点或边。 注意&#xff1a; 每条最短路中至少要包含一条边。 输入格式 第一行包…