一、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 的场景下,这样的拷贝影响比较大。

对应io_uring就有他的优势:

  • 减少系统调用:io_uring通过内核态和用户态共享内存的方式进行通信。如下图:

用户态和内核态之间通过共享内存维护了三部分:提交队列、完成队列、提交队列表项数组。

提交队列是一整块连续的内存空间存储的环形队列,用于存放将要执行I/O操作的数据。

完成队列也是一整块连续的内存空间存储的环形队列,其中存放了I/O操作完成返回的结果。

提交队列表项数组是以数组形式将要执行的I/O操作组织在一起,提交队列完成队列分别通过指针指向对应表项(内部通过偏移量实现)完成对应操作。如下图所示:

提交队列具体实现为:io_uring_sq结构体。

struct io_uring_sq {unsigned *khead;unsigned *ktail;unsigned *kflags;unsigned *kdropped;unsigned *array;struct io_uring_sqe *sqes;
​unsigned sqe_head;unsigned sqe_tail;
​size_t ring_sz;void *ring_ptr;
​unsigned ring_mask;unsigned ring_entries;
​unsigned pad[2];
};

提交队列通过struct io_uring_sqe *sqes提交队列表项数组,使用khead、ktail指向对应队头和对尾完成应用层提交的IO任务的处理。同理完成队列也是如此,不过是由内核态将对应IO事件执行完成后将结果写入到对应表项。应用层通过完成队列表项即可获取到最终结果。

整体流程为:

请求时:1、应用创建SQE,更新SQ tail 2、内核消费SQE,更新SQ head。内核开始处理任务,处理完成后:1、内核为完成的一个或多个请求创建CQE,更新CQ tail 2、应用层消费CQE,更新CQ head。

二、liburing安装

上文介绍的io_uring均为内核层支持,在内核层提供了三个API:

  • io_uring_setup(2)

  • io_uring_register(2)

  • io_uring_enter(2)

    为方便使用直接安装liburing即可在应用层使用io_uring。liburing为作者Axboe封装好的用户态库。

实验环境:vmware 17安装ubuntu22.04 、内核版本:6.8.0-60-generic

选用源码安装方式,安装liburing。

1、获取源码:git clone https://github.com/axboe/liburing.git

2、编译:

cd liburing-master
./configure
make -j
sudo make install

三、代码实现

#include <stdio.h>
#include <liburing.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
​
#define BUFFER_LENGTH   1024
#define ENTRIES_LENGTH  1024
​
#define EVENT_ACCEPT    0
#define EVENT_READ      1
#define EVENT_WRITE     2
​
// io事件信息
struct req_info{int fd;int event;
};
​
// 初始化tcp fd
int init_server(unsigned int port){int socket_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(struct sockaddr_in));serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(port);if(-1 == bind(socket_fd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr))){perror("bind error!\n");}listen(socket_fd, 10);return socket_fd;
}
​
// 设置accept事件
int set_event_accept(struct io_uring *ring, int sockfd, struct sockaddr* addr, socklen_t *addrlen, int flags){struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
​struct req_info accept_info = {.fd = sockfd,.event = EVENT_ACCEPT};
​memcpy(&sqe->user_data, &accept_info, sizeof(struct req_info));io_uring_prep_accept(sqe, sockfd, addr, addrlen, flags);
​return 0;
}
​
// 设置recv事件
int set_event_recv(struct io_uring *ring, int sockfd, void *buf, size_t len, int flags){struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
​struct req_info recv_info = {.fd = sockfd,.event = EVENT_READ};
​memcpy(&sqe->user_data, &recv_info, sizeof(struct req_info));io_uring_prep_recv(sqe, sockfd, buf, len, flags);
​return 0;
}
​
// 设置send事件
int set_event_send(struct io_uring *ring, int sockfd, void *buf, size_t len, int flags){struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
​struct req_info send_info = {.fd = sockfd,.event = EVENT_WRITE};
​memcpy(&sqe->user_data, &send_info, sizeof(struct req_info));io_uring_prep_send(sqe, sockfd, buf, len, flags);
​return 0;
}
​
int main(int argc, void *argv[]){unsigned int port = 9999;int sockfd = init_server(port);
​struct io_uring_params params;memset(&params, 0, sizeof(struct io_uring_params));// 初始化io_uring 其中包含了sq和cqstruct io_uring ring;
​io_uring_queue_init_params(ENTRIES_LENGTH, &ring, &params);
​struct sockaddr_in clientaddr;socklen_t len = sizeof(clientaddr);set_event_accept(&ring, sockfd, (struct sockaddr*)&clientaddr, &len, 0);
​while(1){char buffer[BUFFER_LENGTH] = {0};
​// 事件提交io_uring_submit(&ring);
​// 等待事件完成,其他开发场景下非必要情况无需等待struct io_uring_cqe *cqe;io_uring_wait_cqe(&ring, &cqe);
​// 获取完成事件struct io_uring_cqe *cqe_list[128];int nready = io_uring_peek_batch_cqe(&ring, cqe_list, 128);// echo逻辑处理for(int i = 0; i < nready; ++i){struct io_uring_cqe *entries = cqe_list[i];struct req_info result;memcpy(&result, &entries->user_data, sizeof(struct req_info));if(result.event == EVENT_ACCEPT){printf("accept client\n");set_event_accept(&ring, sockfd, (struct sockaddr*)&clientaddr, &len, 0);
​int connfd = entries->res;
​set_event_recv(&ring, connfd, buffer, BUFFER_LENGTH, 0);}else if(result.event == EVENT_READ){int connfd = entries->res;if(connfd == 0){printf("close fd:%d\n", result.fd);close(result.fd);}else if(connfd > 0){printf("recv: len:%d, data:%s\n", connfd, buffer);set_event_send(&ring, result.fd, buffer, connfd, 0);}else{printf("error recv!\n");close(result.fd);}}else if(result.event == EVENT_WRITE){int ret = entries->res;printf("set_event_send ret: %d, %s\n", ret, buffer);set_event_recv(&ring, result.fd, buffer, BUFFER_LENGTH, 0);}}
​// 清除完成队列中完成表项,以免事件重复处理io_uring_cq_advance(&ring, nready);}
​return 0;
}

更多内容可参考:0voice · GitHub 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/86604.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/86604.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/86604.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【docker】docker run参数说明

功能 拉起容器。 参数 -i&#xff0c;--interactive 保持容器标准输入放开&#xff0c;就算没有终端也放开。 可以理解为可以向容器内输入东西&#xff0c;比如&#xff1a; [rootlocalhost ~]# echo 111 | docker run -i yaxin:1.0 cat 111--cap-add 用于向容器添加特定的…

从0开始学习计算机视觉--Day04--损失函数

在上次学习中&#xff0c;我们知道了线性分类的函数是f(x,W),但并没有解释要怎么得到W权重矩阵的值&#xff0c;以及我们要怎么用训练数据来确定它的最优权重矩阵。在之前我们知道&#xff0c;假设用了10种类别的图片用于训练&#xff0c;将其中一种图片输入模型后&#xff0c;…

【V2.0】TPS-61088升压板-3.7V升压到9V电源板

优化一下上一版本的升压板&#xff1a; TPS-61088升压板-3.7V升压到9V电源板-CSDN博客 改动参考了官方的demo板 加了很多的电容&#xff0c;封装很大&#xff0c;同时去掉了AGND&#xff0c;直接使用一个GND。 补偿电路增加了一个47pF的电容。 EN引脚改用输入的电压分压来启…

基于DeepSeek搭建Dify智能助手国产化架构运行arm64

基于DeepSeek搭建Dify智能助手国产化架构运行arm64 基于DeepSeek搭建Dify智能助手案例介绍案例内容1 概述1.1 背景介绍1.2 适用对象1.3 案例时间1.4 案例流程1.5 资源总览 2.启动 Docker 容器没有的安装2.1没有Docker安装 3 云主机部署DeepSeek3.1 安装Ollama 4.安装Dify4.1Doc…

PyQtNode Editor 第一篇环境配置

PyQtNode Editor 以其独特的功能和灵活的扩展性&#xff0c;吸引了众多开发者的目光。 这篇博客作为系列开篇&#xff0c;将详细介绍开发 PyQtNode Editor 所需的基础环境、安装步骤&#xff0c;同时深入解读一段简单的 PyQt5 代码&#xff0c;为后续的开发工作奠定基础。 一…

Unity 脚本自动添加头部注释

&#x1f4dd; Unity Script Header 注释生成器 一个轻量、可配置的 Unity 编辑器工具&#xff0c;在创建 .cs 脚本时自动插入统一格式的注释头信息。 支持作者、邮箱、公司、地点、版权、描述等字段&#xff0c;所有信息都可通过 Project Settings 界面配置并动态开关。 &…

偏微分方程能量变化分析2

题目 问题 9. 考虑以下带有边界条件的偏微分方程&#xff08;PDE&#xff09;&#xff1a; u t t − c 2 u x x 0 , x > 0 , u_{tt} - c^2 u_{xx} 0, \quad x > 0, utt​−c2uxx​0,x>0, u ∣ x 0 0. u|_{x0} 0. u∣x0​0. 定义能量泛函&#xff1a; E ( t ) …

模型部署和推理架构学习笔记

一. 初步认识模型部署 1. 什么是ONNX&#xff1f; ONNX 就是一个 中间人 或 通用翻译器。它让你在喜欢的框架&#xff08;如 PyTorch&#xff09;里训练好模型后&#xff0c;能轻松地把它变成一种 标准格式。然后&#xff0c;这个标准格式的模型可以被 很多不同的工具和硬件 …

OpenCV CUDA模块设备层-----逐通道的正弦运算函数sin()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 OpenCV 的 CUDA 模块&#xff08;cv::cudev&#xff09; 中的一个设备端数学函数&#xff0c;用于在 CUDA 核函数中对 uchar4 类型&#xff08;即…

人工智能-基础篇-2-什么是机器学习?(ML,监督学习,半监督学习,零监督学习,强化学习,深度学习,机器学习步骤等)

1、什么是机器学习&#xff1f; 机器学习&#xff08;Machine Learning, ML&#xff09;是人工智能的一个分支&#xff0c;是一门多领域交叉学科&#xff0c;涉及概率论、统计学、逼近论、凸分析等数学理论。其核心目标是让计算机通过分析数据&#xff0c;自动学习规律并构建模…

【deepseek】TCP/IP ISO 卸载

TCP/IP 全卸载中的 LSO&#xff08;Large Send Offload&#xff09; 和 LRO&#xff08;Large Receive Offload&#xff09; 是网卡硬件加速技术&#xff0c;其核心目标是 将 TCP/IP 协议栈的处理任务从 CPU 转移到网卡硬件&#xff0c;从而大幅降低 CPU 负载并提升网络性能。以…

抖音小程序支付错误码141211

前情 uni-app是我比较喜欢的跨平台框架&#xff0c;它能开发小程序/H5/APP(安卓/iOS)&#xff0c;重要的是对前端开发友好&#xff0c;自带的IDE让开发体验也挺棒的&#xff0c;公司项目就是主推uni-app 公司今年准备新开一个项目&#xff0c;但是对项目的未来和项目要做的规…

springcloud/springmvc协调作用传递验证信息

微服务架构的拆分&#xff0c;各模块之间使用feign组件来进行相互http转发通信。 前端与后端之间使用springcloud的网关来进行协调。 现在问题出现&#xff0c;用户的信息如何进行传递&#xff1f; 前端请求携带请求头&#xff0c;请求头中的authorization为携带的对应token…

Apache Flink Kafka 写连接器源码深度剖析

一、架构概述 Apache Flink 提供的 Kafka 写入连接器是实现与 Kafka 消息队列集成的关键组件&#xff0c;支持多种语义保证和灵活配置选项。本文将深入分析 Flink Kafka 写入连接器的源码实现&#xff0c;包括架构设计、核心类、事务机制和性能优化等方面。 1.1 整体架构 Fl…

强化学习理论基础:从Q-learning到PPO的算法演进(2)

文章目录 Policy gradient思想(REINFORCE算法)优势函数PPO(Proximal Policy Optimization)Policy gradient思想(REINFORCE算法) 下面我们来探讨一下Policy gradient策略,也就是REINFORCE算法。 在玩剪刀石头布这个简单的游戏中,我们可以有不同的策略。一种是完全随机地…

Oracle数据库文件变成32k故障恢复--惜分飞

最近一个客户数据库重启系统之后,数据文件大小变为了32kb,我接手的不是第一现场(客户那边尝试了rman还原操作),查看alert日志,数据库最初报错 Wed Jun 18 13:09:23 2025 alter database open Block change tracking file is current. Read of datafile D:\APP\ADMINISTRATOR\OR…

移动端 uniapp 写一个可自由拖拽的小键盘

写之前要考虑&#xff1a; 键盘展开后&#xff0c;不能超过手机边缘在底部展开键盘&#xff0c;键盘应出现在展开按钮上方&#xff1b;以此类推重复点击展开按钮&#xff0c;关闭键盘 效果&#xff1a; 代码如下&#xff0c;有些按键逻辑还需要优化 <template><vi…

《二分枚举答案(配合数据结构)》题集

文章目录 1、模板题集2、课内题集3、课后题集1. 字符串哈希2. 并查集3. ST表 1、模板题集 分巧克力 2、课内题集 倒水 冶炼金属 连续子序列的个数 3、课后题集 括号内的整数代表完整代码行数。 1. 字符串哈希 你猜猜是啥题(60) 2. 并查集 拯救萌萌(72) 3. ST表 GCD不小…

PY32F030单片机,优势替代ST GD,主频48MHz,带LED数码管驱动

PY32F030是一款高性能32位单片机&#xff0c;采用ARM Cortex-M0内核&#xff0c;工作频率高达48MHz&#xff0c;具备64KB Flash和8KB SRAM。它支持1.7V~5.5V宽电压范围&#xff0c;集成多路I2C、SPI、USART通讯外设&#xff0c;配备12位ADC、16位定时器和比较器&#xff0c;适用…

Rockchip Uboot中修改固件探测的存储介质

Rockchip Uboot中修改固件探测的存储介质 Rockchip uboot中支持从 eMMC、SDcard、NAND 、SPI_NAND、SPI_NOR等存储介质引导固件。 uboot的spl启动的时候会默认呢都会去探测这些介质&#xff0c;这样会导致探测时间变长&#xff0c;在实际产品中可以根据产品需求进行个性化的配…