Linux异步io机制 io_uring

io_uring作为2019年的后起之秀,为linux异步网络编程新增一把倚天大剑,让我们简单学习一下!

数据结构:

a. sq (submition queue):提交队列,一个存放待执行事件的环形队列

b. cq (completion queue): 完成队列,存放已经完成的事件的环形队列

:这两个队列是用户态和内核态之间共享的内存,使用mmap实现

c. sqe :sq中的一项

struct io_uring_sqe { // sq中的一项

__u64 user_data; // 8 btyes

}

d. cqe:cq中的一项

struct io_uring_cqe { // cq中的一项

​ …

__u64 user_data; // 8 btyes

res; // 任务函数的返回值 eg. recv返回收到数据的长度、accept返回clientfd

}

在这里插入图片描述

io_uring基本工作流程如下:(参照上图)

1.初始化ring,其中包含了sq和cq

2.向提交队列sq里注册(提交)一个事件(任务)sqe

3.将sq中的若干事件(任务)提交给内核处理(submit)

4.内核将处理成功的事件cqe放到完成队列cq里

5.用户取出cq里的完成事件cqe

需要注意的是:

注 : sq里注册的任务sqe一旦交给内核处理,就从sq里被移除了,是一次性的,和epoll不同

注 : cq里的 completion 事件cqe,不主动清除会一直存在

注 : cq队列里都是执行成功的任务,没有执行失败的任务,不用判断recv()返回值 < 0的情况

接下来进行接口分析

io_uring 的三个系统调用liburing 中的封装

  • io_uring_setup (初始化ring)被封装为 io_uring_queue_init_params(entries, &ring, &params);
  • io_uring_register(从sqe向sq里注册一个任务sqe)被封装为 io_uring_prep_recv,io_uring_prep_accept等
  • io_uring_enter(将sq里的任务提交给内核处理)被封装为 io_uring_submit(&ring)

初始化:

struct io_uring_params params; // 用来初始化 ring
memset(&params, 0, sizeof(params)); // 初始化 paramsstruct io_uring ring; // sq and cq 两个环形队列// 下面使用了系统调用io_uring_setup : 初始化ring
io_uring_queue_init_params(1024, &ring, &params); // 初始化两个环形队列:submition queue 和 completion queue

注册(添加)任务(事件):

struct io_uring_sqe *sqe = io_uring_get_sqe(ring); // 获取sq中下一个可用的位置sqestruct user_data user_data = {  // sqe->user_data.fd = sockfd,.event = EVENT_ACCEPT,};io_uring_prep_accept(sqe, sockfd, (struct sockaddr*)addr, addrlen, flags); // 向sq里加入一个任务sqememcpy(&sqe->user_data, &user_data, sizeof(struct user_data)); // 设置任务sqe的属性

提交事件给内核处理:

// 将sq里的任务提交给内核处理io_uring_submit(&ring);

获取完成的事件:

struct io_uring_cqe *cqe; // 指向cqe(cq中的一项)的指针// 等待sq里的任务完成,并返回一个结果io_uring_wait_cqe(&ring, &cqe); // 这个函数会阻塞当前线程,直到至少一个 cqe 可用为止。// 一旦有一个 cqe 可用,它就会将其存储在提供的指针cqe中,并返回// 从完成队列cq获取若干内核处理完成的结果,到cqes数组里,不会阻塞struct io_uring_cqe *cqes[128];int nready = io_uring_peek_batch_cqe(&ring, cqes, 128);  // 和 epoll_wait() 类似/*io_uring_wait_cqe 和 io_uring_peek_batch_cqe 一个是阻塞获取一个结果,一个是非阻塞获取多个结果
*/

移除cq中的完成事件:

// 处理完结果后,将完成的任务从cq中移除io_uring_cq_advance(&ring, nready);

最后附上一个io_uring实现的tcpserver,注释很详细

#include <stdio.h>
#include <liburing.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>/* * io_uring 的三个系统调用在 liburing 中的封装:** io_uring_setup (初始化ring) -->被封装为 io_uring_queue_init_params(entries, &ring, &params); ** io_uring_register(从sqe向sq里注册一个任务sqe) -->被封装为 io_uring_prep_recv,io_uring_prep_send,io_uring_prep_accept* * io_uring_enter(将sq里的任务提交给内核处理)-->被封装为 io_uring_submit(&ring)* * * * 注 : sq里注册的任务sqe一旦交给内核处理,就从sq里被移除了,是一次性的,和epoll不同* 注 : cq里的 completion 事件cqe,不主动清除会一直存在* * 注 : ##cq队列里都是执行成功的任务##,没有执行失败的任务,不用判断recv()返回值 < 0的情况* * 注 : sq和cq都是环形队列,是用户态和内核态的共享内存空间,用mmap实现 (submition queue & completion queue )* * 注 : sqe(submition queue entry), cqe(completion queue entry) --> 这两个分别代表sq、cq中的一项* */// 自定义事件类型 event
#define EVENT_ACCEPT   	0
#define EVENT_READ		1
#define EVENT_WRITE		2/** struct io_uring_cqe { // cq中的一项*     ...*  *     __u64 user_data; // 8 btyes* *     res;  // 任务函数的返回值   eg. recv返回收到数据的长度、accept返回clientfd*    * }* * struct io_uring_sqe { // sq中的一项*     ...* *     __u64 user_data; // 8 btyes* }* */// 自定义 user_data 保证 8 btyes
struct user_data {   // sqe & cqe --> user_dataint fd;int event;
};int init_server(unsigned short port) {	  // create a listener sockfdint sockfd = socket(AF_INET, SOCK_STREAM, 0);	struct sockaddr_in serveraddr;	memset(&serveraddr, 0, sizeof(struct sockaddr_in));	serveraddr.sin_family = AF_INET;	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);	serveraddr.sin_port = htons(port);	if (-1 == bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr))) {		perror("bind");		return -1;	}	listen(sockfd, 10);return sockfd;
}#define ENTRIES_LENGTH		1024
#define BUFFER_LENGTH		1024int set_event_recv(struct io_uring *ring, int sockfd,void *buf, size_t len, int flags) { // 向sqe中添加一个recv任务struct io_uring_sqe *sqe = io_uring_get_sqe(ring); //获取sq中下一个可用的位置sqestruct user_data user_data = {.fd = sockfd,.event = EVENT_READ,};io_uring_prep_recv(sqe, sockfd, buf, len, flags); // 向sq里加入一个recv任务memcpy(&sqe->use_data, &user_data, sizeof(struct user_data)); // 设置recv任务的属性}int set_event_send(struct io_uring * ring, int sockfd,void *buf, size_t len, int flags) { // 向sq里添加一个send任务struct io_uring_sqe *sqe = io_uring_get_sqe(ring); // 获取sq中下一个可用的位置sqestruct user_data user_data = {.fd = sockfd,.event = EVENT_WRITE,};io_uring_prep_send(sqe, sockfd, buf, len, flags); // 向sq里加入一个任务sqememcpy(&sqe->user_data, &user_data, sizeof(struct user_data)); // 设置任务sqe的属性}   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); // 获取sq中下一个可用的位置sqestruct user_data user_data = {  // sqe->user_data.fd = sockfd,.event = EVENT_ACCEPT,};io_uring_prep_accept(sqe, sockfd, (struct sockaddr*)addr, addrlen, flags); // 向sq里加入一个任务sqememcpy(&sqe->user_data, &user_data, sizeof(struct user_data)); // 设置任务sqe的属性}int main(int argc, char *argv[]) {// 服务端监听套接字unsigned short port = 9999;int sockfd = init_server(port);// 客户端套接字地址struct sockaddr_in clientaddr;	socklen_t len = sizeof(clientaddr);struct io_uring_params params; // 用来初始化 ringmemset(&params, 0, sizeof(params)); // 初始化 paramsstruct io_uring ring; // sq and cq 两个环形队列// 下面使用了系统调用io_uring_setup : 初始化ringio_uring_queue_init_params(1024, &ring, &params); // 初始化两个环形队列:submition queue 和 completion queue// 向提交队列sq里添加一个accept任务set_event_accept(&ring, sockfd, (struct sockaddr *)&clientaddr, &len, 0);char buffer[BUFFER_LENGTH] = {0};while (1) {// 将sq里的任务提交给内核处理io_uring_submit(&ring);// 存储内核处理完成的结果在 cq(completion queue)里struct io_uring_cqe *cqe; // 指向cqe(cq中的一项)的指针// 等待sq里的任务完成,并返回一个结果io_uring_wait_cqe(&ring, &cqe); // 这个函数会阻塞当前线程,直到至少一个 cqe 可用为止。// 一旦有一个 cqe 可用,它就会将其存储在提供的指针cqe中,并返回// 从完成队列cq获取若干内核处理完成的结果,到cqes数组里,不会阻塞struct io_uring_cqe *cqes[128];int nready = io_uring_peek_batch_cqe(&ring, cqes, 128);  // 和 epoll_wait() 类似/*io_uring_wait_cqe 和 io_uring_peek_batch_cqe 一个是阻塞获取一个结果,一个是非阻塞获取多个结果
*/// 查看并处理结果int i = 0;for (i = 0; i < nready; i++) {struct io_uring_cqe *entries = cqes[i]; // 已经完成的事件cqestruct user_data result;// 获取完成事件的 user_datamemcpy(&result, &entries->user_data, sizeof(struct user_data));if (result.event == EVENT_ACCEPT) { // accept任务完成// 重新在sq注册一个accept任务set_event_accept(&ring, sockfd, (struct sockaddr *)&clientaddr, &len, 0);int connfd = entries->res;// accept任务的执行结果:clientfd// 在sq注册一个任务:接收客户端数据set_event_recv(&ring, connfd, buffer, BUFFER_LENGTH, 0);} else if (result.event == EVENT_READ) { // read任务完成int ret = entries->res; // read的返回值if (ret == 0) { // 对方发送了fin包通知断开连接close(result.fd);} else if (ret > 0) {//  在sq注册一个任务:向客户端send数据set_event_send(&ring, result.fd, buffer, ret, 0);} else if (result.event == EVENT_WRITE) { // write任务完成int ret = entries->res; // write的返回值// 在sq注册一个任务:接收客户端数据set_event_recv(&ring, result.fd, buffer, BUFFER_LENGTH, 0);}}}// 处理完结果后,将完成的任务从cq中移除io_uring_cq_advance(&ring, nready);}}

推荐学习https://xxetb.xetslk.com/s/p5Ibb

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

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

相关文章

Node.js -- 包管理工具

文章目录 1. 概念介绍2. npm2.1 npm 下载2.2 npm 初始化包2.3 npm 包(1) npm 搜索包(2) npm 下载安装包(3) require 导入npm 包的基本流程 2.4 开发依赖和生产依赖2.5 npm 全局安装(1) 修改windows 执行策略(2) 环境变量Path 2.6 安装包依赖2.7 安装指定版本的包2.8 删除依赖2.…

visual studio2022,开发CMake项目添加rabbitmq库,连接到远程计算机并进行开发于调试

1.打开visual studio installer 。安装“用于 Windows 的 C CMake 工具” 2.新建CMake项目 3.点击VS的“工具”—>"选项“—>“跨平台”—>”连接管理器“,添加远程计算机。用来将VS编辑的代码传到服务器进行编译–连接—运行&#xff08;调试&#xff09;。 …

java中的泛型(三)——通配符

在前面的文章中我们简要介绍了泛型的概念以及泛型类和泛型方法的使用。在介绍泛型时我们说过在在java中一般用E、T、K、V、N、?这几个字母和符号来表示泛型&#xff0c;对于前面的几个字符它们的使用没有区别&#xff0c;只要注意它们所代表的类型就好。而对于最后一个&#x…

优化大型语言模型交互:提升查询和提示效果的26条原则

推荐下arxiv挂的一个提示词教程&#xff1a; https://github.com/VILA-Lab/ATLAS https://arxiv.org/abs/2312.16171 它提出了一套26条指导原则&#xff0c;改善和优化与大型语言模型&#xff08;LLMs&#xff09;的交互过程。通过这些原则&#xff0c;旨在简化对LLMs的查询和…

E4980A是德科技E4980A精密LCR表

181/2461/8938产品概述&#xff1a; Keysight E4980A 精密 LCR 表为各种元件测量提供了精度、速度和多功能性的最佳组合。E4980A 在低阻抗和高阻抗范围内提供快速测量速度和出色的性能&#xff0c;是元件和材料的一般研发和制造测试的终极工具。LAN、USB 和 GPIB PC 连接可提高…

【氮化镓】p-GaN HEMTs空穴陷阱低温冻结效应

这篇文章是关于低温条件下p-GaN高电子迁移率晶体管&#xff08;HEMTs&#xff09;栅极漏电的研究。文章通过电容深能级瞬态谱&#xff08;C-DLTS&#xff09;测试和理论模型分析&#xff0c;探讨了空穴陷阱对栅极漏电电流的影响。以下是对文章的总结&#xff1a; 摘要&#xf…

本地认证的密码去哪了?怎么保证安全的?

1. windows登录的明文密码&#xff0c;存储过程是怎么样的&#xff1f;密文存在哪个文件下?该文件是否可以打开&#xff0c;并且查看到密文&#xff1f; 系统将输入的明文密码通过hash算法转为哈希值&#xff0c;且输入的值会在内存中立即删除无法查看。 然后将密文存放在C:…

基于Spring Boot的火车订票管理系统设计与实现

基于Spring Boot的火车订票管理系统设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 前台首页功能界面图&#xff0c;在系统首页可以查看…

ROS机器人小车建模仿真与SLAM(5)

目录 一、ROS中使用摄像头 1.1 验证摄像头设备可用 1.2 安装cheese 1.3 测试 二、获取摄像头驱动包 2.1 usb_cam 2.1.1 rosrun方式运行 2.1.2 roslaunch方式运行 三、摄像头标定(camera calibration) 3.2 标定前准备 3.2.1 标定板 3.2.2 摄像头调焦 3.3 标定摄像头…

MySQL__索引

文章目录 &#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724 &#x1f389; 主题&#xff1a; MySQL__索引&#xff09; ⏱️ 创作时间&#xff1a;2024年04月23日 ———————————————— 这里写目…

avl excite python二次开发1--python解释器需用内置解释器aws_cmd

avl excite python二次开发1--python解释器需用内置解释器aws_cmd 1、python解释器问题1.1、用外置python解释器&#xff0c;import WSInterface会失败(WSInterface.pyd)1.2、用内置解释器aws_cmd运行py脚本1.3 用内置解释器aws_python执行脚本三级目录 1、python解释器问题 1…

Python_AI库 Pandas的数据结构及基本操作

Python_AI库 Pandas的数据结构及基本操作 本文默认读者具备以下技能&#xff1a; 熟悉python基础知识&#xff0c;vscode或其它编辑工具 熟悉表格文件的基本操作 具备自主扩展学习能力 本文篇幅较长&#xff0c;但比较重要&#xff0c;希望读者可以认真看完并实例运行。 Pa…

Hugging Face 新开源Parler-TTS,高质量文本转语音模型,可自主训练定制声音,训练代码、模型、数据集全部公开

前言 Parler-TTS 是大名鼎鼎的huggingface推出的一款轻量级文本到语音&#xff08;TTS&#xff09;模型&#xff0c;它能够生成高质量、自然流畅的语音&#xff0c;并且能够模仿特定说话者的风格&#xff0c;包括性别、音高、说话风格等。这款模型是由Dan Lyth和Simon King创建…

【机器学习原理】决策树从原理到实践

基于树的模型是机器学习中非常重要的一类模型&#xff0c;最基础的就是决策树&#xff0c;本篇主要讲述决策树的原理和几类最常见的决策树算法&#xff0c;这也是更复杂的树模型算法的基础。 参考文章&#xff1a; 1.CSDN-基于熵的两个模型(ID3,C4.5)比较详细&#xff0c;有数字…

PotatoPie 4.0 实验教程(22) —— FPGA实现摄像头图像对数(log)变换

什么是图像的log变换&#xff1f; 总的来说&#xff0c;对数变换是一种常用的图像增强技术&#xff0c;可以改善图像的视觉质量、减少噪声以及突出图像中的细节&#xff0c;从而提高图像在视觉感知和分析中的效果和可用性。 图像的对数变换&#xff08;log transformation&am…

【Linux命令行艺术】1. 初见命令行

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

Anti Rookit -- 检测隐藏进程

Anti Rookit 一&#xff1a;检测隐藏进程 引言 检测隐藏进程除了众所周知的枚举进程ID之外&#xff0c;还有枚举句柄表的方式。不过今天给大家带来的是第三种方法。 探究 应用层通过接口 C r e a t e P r o c e s s \textcolor{cornflowerblue}{CreateProcess} CreateProcess…

【Linux系统编程】26.信号、kill、alarm、setitimer

目录 信号 信号共性 信号特质 产生信号 信号相关概念 默认处理动作 信号4要素 常规信号 ​编辑 注意 kill 参数pid 测试代码1 测试结果 测试代码2 测试结果 alarm 参数seconds 返回值 取消闹钟 测试代码3 测试结果1 测试结果2 ​编辑 setitimer 参数…

BIO、NIO与AIO

一 BIO 同步并阻塞(传统阻塞型)&#xff0c;服务器实现模式为一个连接一个线程&#xff0c;即客户端有连接请求时服务器端就需要启动一个线程进行处理. BIO&#xff08;Blocking I/O&#xff0c;阻塞I/O&#xff09;模式是一种网络编程中的I/O处理模式。在BIO模式中&#xf…

物联网实战--平台篇之(一)架构设计

本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html 一、平台简介 物联网平台这个概念比较宽&#xff0c;大致可以分为两大类&#x…