与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任务则会给它赋值传输字节流的量。

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