与epoll媲美的io_uring

简介: 与epoll媲美的io_uring

前导知识

同步与异步:在编程中,异步指的是两者之间的关系。这种关系是【无需马上进入等待】。什么意思呢?举个例子,比如服务器和客户端之间,如果是同步关系,那么一般情况是客户端发送报文,服务器处理之后返回应答报文,这期间客户端等待,无所事事,收到返回报文之后再继续发送下一笔报文。异步关系下,客户端不需要等待服务器有没有返回报文,可以自行选择继续发送报文或者等待。当然异步可以不只是客户端和服务器之间的关系,也可以是执行函数与执行函数之间的关系,A函数可以在B函数阻塞时直接接手执行,不等待B函数执行完成。只要能确定两个事物,就能确定他们的关系。

常规IO函数的隐藏步骤:read、write、recv、send都是常规的IO函数,它们底层其实都分了两个步骤,一是发起读写请求,二是进行数据的拷贝(读函数将数据从内核缓冲区拷贝进用户空间,写函数将数据从用户空间拷贝入内核缓冲区),并进行结果返回。所以当第一步执行之后,因为没有数据等原因,无法进行第二部,那么通常会阻塞住,造成资源开销。这两步无法隔离。

异步IO和非阻塞IO:异步IO一定是非阻塞IO,非阻塞IO不一定是异步IO。异步IO是可以在没有数据可以进行IO的时候,用一些方法进行跳转,暂时先不进行读写操作的。非阻塞IO除了异步IO的情形,还可以是在没有读写数据的时候,直接返回异常值。

环形缓冲区:也叫循环队列,比如一个队列有5个位置,之前存入5个数据会依次占满队列,那么当第六个数据存入时,会计算6除以5余1·,于是第六个数据存入第一个位置并覆盖原有数据。

Io_uring的概念与逻辑

Io_uring: Input/Output user ring。可以直译为,用户能使用的用于输入输出的环形缓冲区。而个环形缓冲区的实现和使用,又会涉及到异步等技术栈。

Io_uring逻辑简图:

如图,两个queue都是环形缓冲区。当有任务请求的时候,会先储存到submit queue中。之后调用submit函数会从submit queue里将请求批量取出交给内核执行,内核执行完成之后再将已完成任务放置到complete queue里等待清除。这些任务包括,读取、写入、连接等。

io_uring的优点

异步IO提高程序执行效率。而io_uring会先将io任务存入内核中的submit队列,发起请求后程序可以异步地继续执行其他任务和操作,直到io_uring_sumbit函数一次性将所有请求提交至内核进行处理,内核会异步地执行这些任务,哪怕有些请求暂时没法处理也不会阻塞整个程序的执行。一方面比起多线程技术会减少线程切换、建立、关闭等开销,另一方面比起同步IO省去了阻塞造成的开销。

零拷贝技术。Io_uring中,提交读写请求的时候,还会伴随着提交一个buffer的地址,之后内核处理这些读写请求的时候,会直接从这个buffer里读取数据,或者直接往这个buffer写入数据。相比起以往的常规模型,先将数据存储在内核缓冲区,复制进入应用层,少了复制的步骤(详情可见下文)。并且在创建queue的时候也是直接创建在内核中,用户对队列的操作都是直接对队列进行的,避免了产生复制。

Io_uring构建tcp服务器

构建一个客户端发送什么,就会返回什么的服务器。

导入相关的包与库,设置相关结构体和宏

tcp服务器初始化

-

创建io_uring队列

先初始化一个与io_uring队列的参数相关的结构体。然后用这个参数结构体在内核中去创建并初始化一个长度为ENTRIES_LENGTHD(1024)的io_ring队列,这个队列的引用存储在ring中。也就是说可以通过ring去访问io_uring队列。Io_ring队列是有submit queue和complete queue组成的,它们的长度也都为1024。

提交accept任务

Sqe是submit queue entry,entry是元素的意思。先通过io_uring_get_sqe获取到submit queue的一个元素的指针,然后通过io_uring_prep_accept修改这个struct io_uring_sqe实例的属性,将它变为一个accept请求。Seq->user_Data可以存一些自定义数据。

提交请求并取出执行结果

通过io_uring_submit将submit queue中的请求批量提交。

io_uring_wait_cqe(&ring,&cqe)的作用是阻塞当前线程,直到至少有一个请求完成,这句代码非必要,但在这个小项目中笔者选择了引用。

io_uring_peek_batch_cqe(&ring,cqes,128)获取至多128个已完成的事件,并且存入cqe队列。

遍历cq队列,提交新的请求

提交请求的时候,都会设置user_data,这个user_data在请求完成之后都会带到complete queue的元素中。遍历complete queue的元素,取出其中的user_data,根据这些user_data进行下一步操作。如果完成的是accept事件那就提交recv请求,如果完成的是recv事件那么提交send的请求,send请求在下一轮轮询中随即就会被完成掉。完成了send请求之后那么又继续提交recv请求。最后用io_uring_cq_advance()清空cq队列,advance是前进的意思,通过调整指针指向的方向来表示队列的释放。entries->res是执行结果,accept任务会给entries->res赋值clentfd,send和recv任务则会给它赋值传输字节流的量。

目录
相关文章
|
6月前
|
网络协议 安全 测试技术
手撕测试tcp服务器效率工具——以epoll和io_uring对比为例
手撕测试tcp服务器效率工具——以epoll和io_uring对比为例
132 2
|
6月前
|
存储 Linux 调度
io复用之epoll核心源码剖析
epoll底层实现中有两个关键的数据结构,一个是eventpoll另一个是epitem,其中eventpoll中有两个成员变量分别是rbr和rdlist,前者指向一颗红黑树的根,后者指向双向链表的头。而epitem则是红黑树节点和双向链表节点的综合体,也就是说epitem即可作为树的节点,又可以作为链表的节点,并且epitem中包含着用户注册的事件。当用户调用epoll_create()时,会创建eventpoll对象(包含一个红黑树和一个双链表);
123 0
io复用之epoll核心源码剖析
|
6月前
|
存储 网络协议
TCP服务器 IO多路复用的实现:select、poll、epoll
TCP服务器 IO多路复用的实现:select、poll、epoll
88 0
|
6月前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
Linux C/C++ 开发(学习笔记十二 ):TCP服务器(并发网络编程io多路复用epoll)
114 0
|
6月前
|
存储 Linux
图解IO多路复用模型之select、poll、epoll
图解IO多路复用模型之select、poll、epoll
116 0
|
1月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
23 0
Linux C/C++之IO多路复用(poll,epoll)
|
4月前
|
安全 Java Linux
(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析!
IO(Input/Output)方面的基本知识,相信大家都不陌生,毕竟这也是在学习编程基础时就已经接触过的内容,但最初的IO教学大多数是停留在最基本的BIO,而并未对于NIO、AIO、多路复用等的高级内容进行详细讲述,但这些却是大部分高性能技术的底层核心,因此本文则准备围绕着IO知识进行展开。
165 1
|
4月前
|
存储 Java Unix
(八)Java网络编程之IO模型篇-内核Select、Poll、Epoll多路复用函数源码深度历险!
select/poll、epoll这些词汇相信诸位都不陌生,因为在Redis/Nginx/Netty等一些高性能技术栈的底层原理中,大家应该都见过它们的身影,接下来重点讲解这块内容。
|
6月前
|
NoSQL Java Linux
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
【Linux IO多路复用 】 Linux 网络编程 认知负荷与Epoll:高性能I-O多路复用的实现与优化
183 0
|
6月前
|
消息中间件 架构师 Java
性能媲美epoll的io_uring
性能媲美epoll的io_uring
225 0